So as I mentioned yesterday I have been thinking of using Task Scheduler to try and enable disabled Office add-ins automatically.
The Problem
Here’s what happens. Every so often when users open Word, Outlook, Excel, etc they get a prompt like the one below:
We don’t know why they keep getting it, but ideally users should be clicking “NO” as we use all these add-ins and don’t want them disabled. But users being users they click “YES” intentionally or inadvertently and then call IT with complaints that their Word, Outlook, etc aren’t working as expected. Then we in IT have to do the boring task of going to their add-ins screen and enabling these items; followed by closing and opening Word, Outlook, whatever.
What I want to do here is (1) warn users when the above dialog box comes up, advising them not to click the “YES” button; and (2) if they do click “YES” then somehow enable the add-ins behind their back and get them to close and open the affected program.
Add-ins and the registry
I started off thinking I might need to do some fancy stuff to enable add-ins, but soon realized that they are all controlled by the registry. Nice!
- Add-ins can be installed for all users on the machine or just a specific user. So you have to look under both
HKLM
andHKCU
. The two locations are:HKCU\Software\Microsoft\Office\(application)\Addins\
andHKLM\Software\Microsoft\Office\(application)\Addins\
(replace(application)
with Word, Outlook, Excel, or PowerPoint). These two locations contain keys for each add-in installed for that particular application.Here’s a screen shot for the Outlook InterAction add-in for a user. - The key thing in the registry keys above is the
LoadBehavior
entry. This can take many values – the default is a value of 3 which means the add-in is loaded automatically at startup and is currently loaded. If the value is 2 it means the add-in is loaded automatically at startup but is currently not loaded. - My initial hunch was that
LoadBehavior
is what I must target. I thought when a user disables an add-in theLoadBehavior
value of that add-in probably changes to 2, so all I need to do is switch it back to 3. But I was wrong (as I quickly figured after crashing Word a couple of times and choosing to disable add-ins). - Then I discovered that add-ins which are disabled via a user dialog box as above don’t have their
LoadBehavior
value changed; rather they are set as disabled at another place in the registry. And that is under theHKCU
hive, atHKCU\Software\Microsoft\Office\(version)\(application)\Resiliency\DisabledItems
(replace(version)
with 15.0 for Office 2013, 14.0 for Office 2010, 12.0 for Office 2007, 11.0 for Office 2003, you get the idea … and replace(application)
with Word, Outlook, etc as before).
Each time an add-in is disabled, a REG_BINARY
entry is created under HKCU\Software\Microsoft\Office\(version)\(application)\Resiliency\DisabledItems
with a binary value. I didn’t experiment to see if there’s a one-to-one mapping between this entry and an add-in, and also whether the entry is same for an add-in across all machines; the important finding for me was that if I delete this entry, it enables the add-in. So all I really needed to do to see if there are disabled add-ins for a program is to just check this key, and if there are any entries I can delete them all to re-enable all the add-ins. Nice! This doesn’t require a PowerShell script really. Good old reg
can do the trick too. So a batch file is more than sufficient.
Below is what I came up with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
@ECHO OFF setlocal enableextensions enabledelayedexpansion :CHECKOUTLOOK FOR /F "usebackq" %%I IN (`reg query HKCU\Software\Microsoft\Office\14.0\Outlook\Resiliency\DisabledItems`) DO ( IF %%I NEQ "" GOTO ENABLEOUTADDIN ) :CHECKWORD FOR /F "usebackq" %%I IN (`reg query HKCU\Software\Microsoft\Office\14.0\Word\Resiliency\DisabledItems`) DO ( IF %%I NEQ "" GOTO ENABLEWORDADDIN ) :CHECKEXCEL FOR /F "usebackq" %%I IN (`reg query HKCU\Software\Microsoft\Office\14.0\Excel\Resiliency\DisabledItems`) DO ( IF %%I NEQ "" GOTO ENABLEEXCELADDIN ) :CHECKPPT FOR /F "usebackq" %%I IN (`reg query HKCU\Software\Microsoft\Office\14.0\PowerPoint\Resiliency\DisabledItems`) DO ( IF %%I NEQ "" GOTO ENABLEEXCELADDIN ) GOTO EOF :ENABLEOUTADDIN reg delete HKCU\Software\Microsoft\Office\14.0\Outlook\Resiliency\DisabledItems /va /f msg * "Your Outlook had some errors which the system fixed automatically. Please close and open Outlook. Until you do so certain functionality may be disabled." GOTO CHECKWORD :ENABLEWORDADDIN reg delete HKCU\Software\Microsoft\Office\14.0\Word\Resiliency\DisabledItems /va /f msg * "Your Word had some errors which the system fixed automatically. Please close and open Word. Until you do so certain functionality may be disabled." GOTO CHECKEXCEL :ENABLEEXCELADDIN reg delete HKCU\Software\Microsoft\Office\14.0\Excel\Resiliency\DisabledItems /va /f msg * "Your Excel had some errors which the system fixed automatically. Please close and open Excel. Until you do so certain functionality may be disabled." GOTO CHECKPPT :ENABLEPPTADDIN reg delete HKCU\Software\Microsoft\Office\14.0\PowerPoint\Resiliency\DisabledItems /va /f msg * "Your PowerPoint had some errors which the system fixed automatically. Please close and open PowerPoint. Until you do so certain functionality may be disabled." GOTO EOF :EOF exit /B |
I could probably make it shorter if I pick up more batch file scripting but this does the trick for me.
Let’s break it down for Outlook. I enumerate the registry key. The results of this are iterated over by a FOR
loop. If the list isn’t empty – i.e. there are disabled add-ins – the loop exits and goes to a different section.
1 2 3 4 |
:CHECKOUTLOOK FOR /F "usebackq" %%I IN (`reg query HKCU\Software\Microsoft\Office\14.0\Outlook\Resiliency\DisabledItems`) DO ( IF %%I NEQ "" GOTO ENABLEOUTADDIN ) |
This section that the loop exits to simply deletes all entries under that key, shows a message to the user asking them to close and open Outlook, and goes up the batch file to check Word next.
1 2 3 4 |
:ENABLEOUTADDIN reg delete HKCU\Software\Microsoft\Office\14.0\Outlook\Resiliency\DisabledItems /va /f msg * "Your Outlook had some errors which the system fixed automatically. Please close and open Outlook. Until you do so certain functionality may be disabled." GOTO CHECKWORD |
Repeat and rinse for each application and there you have it! Like I said, easy peasy, quick and dirty!
Maybe I should improve the script later – I don’t know. An overhaul using PowerShell might be a good idea I think. Specifically, there are a few things I am already thinking of adding:
- Currently the message box goes away if the user clicks OK. Would be nice if I could keep an eye out for whether they actually close the program later, and if not keep popping up reminders – maybe a ballon tip?
- Would be nice if I could close the application for the user. I could do that in the batch file too via
taskkill
but I was wary of doing that lest it cause any corruptions. I wouldn’t want Outlook OST and PST files or Word templates getting corrupted because I closed the program. Maybe PowerShell has a more graceful way of shutting down an app? I don’t think so, but that could be my ignorance. - Currently I enable all add-ins. While that’s fine for our case maybe I should have a list of add-ins that are to be excluded from being enabled? Or only enable a whitelist of add-ins?
So that’s that. Next step is to find a way to trigger the batch file.
Scheduled task triggers and Event Viewer
Each time the dialog box asking a user to disable an add-in appears, an entry is logged in Event Viewer. This looked like a promising way to hook on to that event and warn the user not to click “YES”. These entries are logged under “Application and Services Logs” > “Microsoft Office Alerts”.
The joy of discovering that was short lived though, as I realized the same sort of alerts are generated for many other Office related things: spell check notifications, connectivity to Exchange, etc.
All these alerts had the same source and ID and there was nothing for me to distinguish between them! Sure the data part was different, and while Event Viewer (and thus Task Scheduler) supports XPath for searching the Event Logs it has limitations in that I can’t do wildcard searches. Thus while I could do an XPath search for all events that have the exact error message in the first Event Log entry above, I can’t do an XPath search for all events that contain the text “Do you want to disable this add-in?”. This is a big limitation because now I will have to (a) find the exact message for each add-in and program, and (b) create XPath queries for each of these – eugh! That’s too much work for now (and not very elegant either!) so I decided to not pursue that route.
Once a user decides to enable or disable an add-in though, I found a different alert is generated for that. Under the “Application” log, from source “Microsoft Office 14” (since we have Office 2010), alerts with event ID 2001 are generated each time a user selects to disable an add-in (and alerts with event ID 2000 are generated when they choose not to disable).
That’s convenient, coz now I can create a task that is triggered on such events and whose action is to run the previous batch file. Nice!
Putting it all together
End result is a task I can import as in the case of the laptop lid issue. Here’s the XML file of the task:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?xml version="1.0" encoding="UTF-16"?> <Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo> <Date>2014-09-17T11:01:41.2438879</Date> <Author>RAKHESH.LOCAL\Admin</Author> </RegistrationInfo> <Triggers> <EventTrigger> <ExecutionTimeLimit>PT1M</ExecutionTimeLimit> <Enabled>true</Enabled> <Subscription><QueryList><Query Id="0" Path="Application"><Select Path="Application">*[System[Provider[@Name='Microsoft Office 14'] and EventID=2001]]</Select></Query></QueryList></Subscription> </EventTrigger> </Triggers> <Principals> <Principal id="Author"> <UserId>--REPLACE--</UserId> <LogonType>InteractiveToken</LogonType> <RunLevel>LeastPrivilege</RunLevel> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries> <AllowHardTerminate>true</AllowHardTerminate> <StartWhenAvailable>false</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <IdleSettings> <StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle> </IdleSettings> <AllowStartOnDemand>true</AllowStartOnDemand> <Enabled>true</Enabled> <Hidden>false</Hidden> <RunOnlyIfIdle>false</RunOnlyIfIdle> <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine> <WakeToRun>false</WakeToRun> <ExecutionTimeLimit>PT0S</ExecutionTimeLimit> <Priority>7</Priority> </Settings> <Actions Context="Author"> <Exec> <Command>C:\NRPortbl\enable-addins.bat</Command> <Arguments>2>nul</Arguments> </Exec> </Actions> </Task> |
Once again, I install it for the users I want via a batch file like this:
1 2 3 4 5 6 7 8 9 |
copy /Y "\\server\AddIn-Fix\enable-addins.bat" C:\NRPortbl powershell -Command "(gc \\server\AddIn-Fix\schtask.xml) -replace '--REPLACE--', $("$env:USERDOMAIN" + '\' + "$env:USERNAME") | Out-File -Encoding ASCII C:\schtask.xml" schtasks /Create /XML "C:\schtask.xml" /TN "AddIn-Fix" del /f C:\schtask.xml schtasks /Run /TN "AddIn-Fix" pause |
Not done yet!
I am not done yet though. It’s not practical installing it like this for each user in my domain. I chose the above approach just to get it installed for a few test users; next step is to roll it out via GPO to the whole domain. Stay tuned for a post on that later …!
And one more thing …
Something I learnt while reading about add-ins.
- Outlook 2013 will automatically disable add-ins that slow it down.
- Both Outlook 2010 and Outlook 2013 generate “Application” event log entries that show how much time the add-ins take to load. The latter is especially interesting to me. Good way to ferret out slow add-ins.
Update
Thanks to Adam Fowler and Daniel Steefkerk for mentioning this blog post on Twitter and pointing me out to two other tools that do similar things:
- OfficeIns (from NirSoft) – lets you enumerate and enable/ disable Office add-ins, even remotely. Nice!
- Office Add-In Monitor – a small tray utility that lets you specify a list of add-ins to monitor and will “protect” them from being disabled.
I haven’t tried either of these but thought I’d link them here for completeness. Speaking of completeness, if you are on Office 2013 and above you now get a much better prompt when an add-in is disabled, and you have the option of enabling it from there.