Contact

Subscribe via Email

Subscribe via RSS

Categories

Creative Commons Attribution 4.0 International License
© Rakhesh Sasidharan

Elsewhere

Fixing “The task XML contains too many nodes of the same type” error

I imported a scheduled task into Windows Task Scheduler yesterday and that had some incorrect XML because of which each time I’d open Task Scheduler it would throw the following error at me: The task XML contains too many nodes of the same type. What does that mean? No idea! Going through the XML file I imported from, I noticed some errors. So perhaps that’s the reason.

Ok, so can I delete this task from Task Scheduler? Nope! Each time I open Task Scheduler I get the above error but I don’t get to see the task.

What about schtasks? I can see the task through that but I can’t make any changes!

I tried creating a new task to over-write the existing one, but schtasks complains that the task already exists. (Update: While writing this post I came across the the /f switch to schtasks /create – maybe that would have helped?)

Finally here’s what I did.

I found that all the scheduled tasks are located in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks. There are various subkeys here of a format {GUID}. Within each of these there is a value of name Path whose data portion contains the task name (with a folder name if the task is in a folder).

Apart from that we have HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\ which has subkeys corresponding to each task. Each of these subkeys has a value of name Id whose data is the GUID found above.

I deleted the subkeys corresponding to this problem task from both the locations above. That helped as Task Scheduler stopped complaining when I opened it. And which schtasks still shows the task, that just seems to be a skeleton of the actual thing:

Unfortunately I still can’t delete it via <code>schtasks</code>:

However, now I can overwrite this task. So I created a dummy task over-writing this one and then deleted it!

Update: While writing this post I discovered that c:\Windows\System32\Tasks is where the XML files for tasks are stored. So it looks like if I had deleted the task from this folder – apart from the registry keys above – that should have taken care of it.

Hope this helps someone!

Enabling disabled Office add-ins automatically

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:

addin-error

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 and HKCU. The two locations are: addin-registryHKCU\Software\Microsoft\Office\(application)\Addins\ and HKLM\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 the LoadBehavior 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 the HKCU hive, at HKCU\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:

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.

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.

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:

  1. 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?
  2. 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.
  3. 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”.

addin-ev-alert

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.

misc-ev-alert

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).

2001-alert

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:

Once again, I install it for the users I want via a batch file like this:

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.

schtasks specifying username to run an imported task under

Note to self: if you want to import a Task Scheduler task that will “run as” a different user you must specify both the “run as” user name and password on the command line. The XML file containing the task already contains the user name but that won’t cause schtasks to prompt for a password. Here’s an example command line:

If you don’t want to specify the password leave it blank or put an asterisk * instead. This will cause schtasks to prompt for a password.

There isn’t a way to specify a password in the XML file itself. Here’s the relevant snippet from the XML file above where the “run as” user is specified:

security options

If you compare with the GUI, the child element LogonType corresponds to the “Run only when user is logged on” and “Run whether user is logged on or not” options. It can take one of three values:

  • S4U: Which corresponds to “Run whether user is logged on or not” and “Do not store password”. In this case the user account is expected to be a local service account. The account won’t have access to any network resources and its password isn’t stored by the system.
  • Password: Which corresponds to “Run whether user is logged on or not”. If this option is checked in the GUI a password is prompted. But if this option is present in the XML file a password is not prompted and must be entered via the schtasks command as above.
  • InteractiveToken: Which corresponds to “Run only when user is logged on”. The task will only run in an interactive session in this case.

In the first two cases the UserId child element specifies the username under which the task will run. In the second case though the username must be specified again when using the schtasks command even if you specify it in the UserId element.

Hope this helps somebody.

Changing what happens when a laptop lid is closed … (part 2)

Right, so yesterday I posted about this batch file I created to set a laptop to sleep or do nothing depending on whether it’s out of office or in office (which the batch file determines by pinging servers in each network).

What I want to do now is automate the running of this batch file. I don’t want to have the user clicking on this file. Since the file only needs to run each time a change in the network state happens ideally something should just trigger it to run when the state changes.

Use Task Scheduler

I know the Windows Task Scheduler is quite powerful and can trigger tasks on Event Log entries. So if I can find the Event Log entries generated when I connect and disconnect the network, am good to go! I checked the Event Logs on the user laptop and sure enough there were entries. For the wired port they seemed to be tied to the card driver and generated by the driver, but for the wireless port they were generated by Windows itself. Here’s what I found:

  • Event ID 33 is generated in the System log from source “e1dexpress” whenever the LAN port is connected to a network.
  • Event ID 27 is generated in the System log from source “e1dexpress” whenever the LAN port is disconnected to a network.
  • Event ID 8001 is generated in the Applications and Services > Microsoft > Windows > WLAN-AutoConfig > Operational log from source “WLAN-AutoConfig” whenever the WiFi is connected to a network.
  • Event ID 8003 is generated in the Applications and Services > Microsoft > Windows > WLAN-AutoConfig > Operational log from source “WLAN-AutoConfig” whenever the WiFi is disconnected from a network.

Based on this I can create a new scheduled task entry to run my batch file and add triggers for the Event Log entries above. Here’s an example:

image002

And here’s the final list with all the triggers:

image001

Notice I added a trigger to run when the user logs in too. Added this for situations where the user may dock the laptop while it’s powered off and then login. In such I wasn’t sure whether the network connect event log would be generated or not and whether it would be generated before the Task Scheduler runs – was too lazy to test really, so I added an entry to run when the user logs in.

That’s it really! Once such a task is created it runs the batch file as expected.

I did two additional things after creating the task:

  1. By default tasks are set to run only if the computer is on AC power. Since we want it to run when on battery too, be sure to untick that.
  2. Right click the task and export it. This way I can easily import it on other similar laptops. For dis-similar laptops (where the network card model may be different) I’ll have to import and add additional triggers. Still, better than recreating the task from scratch, and I think what I’ll do is add triggers for such models too to the task I created so eventually I’ll have a task that can handle all models in our firm. This way I can import it to any machine without worrying about the model differences.

Importing task on a remote machine

Here’s what I for users who want this on their laptop: I copy the batch file and list of IPs to a folder on the user machine; then I launch Task Scheduler on my machine, connect to the Task Scheduler on user machine, import the task I made above. I don’t even have to visit the user machine, how cool is that!

Importing task on a local machine

If I am at a user machine and want to set them up with the batch file, I don’t want to bother with copying the files and importing a task. I want to make that an easy step too so I created a batch file that copies over the two files and imports the task and runs it once! Just double click the install batch file in such cases and it will do the rest.

Quite obvious what’s happening. The trick here is to use the schtasks command which is a command line interface to manage Scheduled Tasks. Using schtasks I can import the task and run it.

I can use schtasks with a remote computer too so one of these days I’ll probably create another batch file to handle remote additions too.

That’s all for now!

Update: See this post for an updated version of the batch file as well as the XML file I use to import the task.