Contact

Subscribe via Email

Subscribe via RSS/JSON

Categories

Recent Posts

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.

Windows Task Scheduler quirks; Changing what happens when a laptop lid is closed … (part 3)

The other day I posted on how I use Task Scheduler to run a batch file that changes what happens to a laptop when the lid is closed depending on where it is located.

I have been fiddling with the Task Scheduler since then and thinking of how I can use it for other tasks. Specifically, at work we have a lot of add-in issues with Office products wherein every so often Word/ Excel/ Outlook/ PowerPoint give a message when launching that such and such add-in caused issues and it is recommended the user disables them; users click “Yes” to such messages inadvertently or intentionally, after which whatever functionality the add-in provides does not work any more and we at IT get calls! I had an idea to create a PowerShell script that will re-enable the add-in in such cases and maybe use Task Scheduler to hook into any Event Log entries that are generated when add-ins are disabled to trigger the running of this script. That’s the topic of another post however but as a result of exploring this I have been playing with Task Scheduler a bit and revisited the Task Scheduler entry I made for the laptop lid closing problem.

First off, here’s an exported version of the Task Scheduler entry I created:

Note the highlighted lines:

  • Line 5 – the “Author” – doesn’t matter much. It merely specifies who the author of the Task is.
  • Line 23 – the “UserID” – matters. This is the user ID under which the task runs. If you specify a user ID here, you must use the same ID when importing the task. You could specify a user ID here and specify a different ID when importing (via schtasks /ru ...) and the latter will over-ride the ID in the XML file. If you will always be specifying a user ID when importing, you can skip the user ID in the XML file too. I chose to leave it commented so I remember it for later.
  • Line 17 – yet another “UserID” – matters. This user ID is to do with the “run when a user logs in” trigger. If your task doesn’t have such a trigger this user ID won’t be present. You have two choices here really: either you could specify a user ID and that is the user account whose login will trigger the task, or you could skip that tag (remove the whole line) and the task will trigger when any user account logs in. The latter has a catch though. If you do this, then you cannot skip the UserID of line 23. In my case I chose to use a marker here “–REPLACE–” and modify the XML file to replace this with a user ID later on (see below).

Gist of the matter for my specific scheduled task entry is that I can’t import this task willy for any user. When importing I have to (1) specify the user in whose context the task will run – I must do that via schtasks /ru ... or modify the XML file directly – and (2) if I am not modifying the XML file in the previous step, I can’t leave the UserID element of line 17 blank and so I must modify the XML file to put a user ID here. This user ID would be that of the user I am installing it for of course.

To that end I modified the install batch file I had created:

The important bit here is the 4th line, where I use PowerShell to read the XML file and replace the bit that says “–REPLACE–” (which is the marker I use at line 17 of the XML file) with the user ID of the user I am installing this task for. Apart from that, on line 6 when I import the task I get the user ID from the environment variables and prompt for a password. This way the install batch file and task XML files are generic and I can run them for any user.

That’s all for now!

Update: I realized I could simplify things further since I am using PowerShell any ways to replace text in the task scheduler XML file. I can specify the “run as” user too and thus avoid prompting for a password when installing! :)

Notice I have set the marker “–REPLACE–” in two places now. Both will get replaced while installing. The batch file can be modified as below to omit the /RU & /RP switches:

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.