1
votes

Trying to read the last succesful Windows Update time from a remote machine, but getting an error on the key

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\

sample code:

var hive = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
var soft = hive.OpenSubKey("SOFTWARE");
var micro = soft.OpenSubKey("Microsoft");
var wind = micro.OpenSubKey("Windows");
var currver = wind.OpenSubKey("CurrentVersion");
var wu = currver.OpenSubKey("WindowsUpdate"); // returns NULL
var au = wu.OpenSubKey("Auto Update"); // throws exception "Object referece not set to an instance of an object"
var res = au.OpenSubKey("Results");
var inst = res.OpenSubKey("Install");
var lastUpdate = inst.GetValue("LastSuccessTime").ToString();
Console.WriteLine(lastUpdate);

I have verified the key is correct, and I'm not sure what the problem is.

enter image description here

EDIT The error I receive is

Object reference not set to an instance of an object.

because the subkey "WindowsUpdate" is returning NULL.

1
32 vs 64 bit os and different subkeys?Steve
Have you looked at this in the debugger? Are you sure that you are really looking in the right hive? There are 2, one for Win64 and one for Win32, and its not apparent from how you open it which one you are looking in. It seems that wu is null.Ron Beyer
FYI there is a remotable API for this, E.g. stackoverflow.com/questions/15786294/…Alex K.
OpenRemoteBaseKey takes a third parameter: RegistryView.Johnny Mopp
Would be good for you to answer with your solution and codeAlex K.

1 Answers

2
votes

The reason I was getting NULL from the OpenSubKey() method was because I needed to add a RegistryView parameter to OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName, RegistryView.Default|64|32);

Thanks to comments from Alex K and this StackOverflow Answer, I was able to resolve my issue by replacing my code with the following static methods. Just add a reference to WUApiLib.dll, then

using WUApiLib;
public static IEnumerable<IUpdateHistoryEntry> GetAllUpdates(string machineName)
{
    Type t = Type.GetTypeFromProgID("Microsoft.Update.Session", machineName);
    UpdateSession session = (UpdateSession)Activator.CreateInstance(t);
    IUpdateSearcher updateSearcher = session.CreateUpdateSearcher();
    int count = updateSearcher.GetTotalHistoryCount();
    IUpdateHistoryEntryCollection history = updateSearcher.QueryHistory(0, count);
    return history.Cast<IUpdateHistoryEntry>();
}
public static DateTime GetLastSuccessfulUpdateTime(string machineName)
{
    DateTime lastUpdate = DateTime.Parse("0001-01-01 00:00:01");
    var updates = GetAllUpdates(machineName);
    if (updates.Where(u => u.HResult == 0).Count() > 0)
    {
        lastUpdate = updates.Where(u => u.HResult == 0).OrderBy(x => x.Date).Last().Date;
    }
    return lastUpdate;
}

To use,

DateTime lastSuccessfulUpdate = GetLastSuccessfulUpdateTime("PC-01");

NOTE: For reference, this only returns the single most recent, successful update package's timestamp. It does not mean that all other Windows Updates have been successful. In order to get a list of failed updates, use the following:

IList<IUpdateHistoryEntry> failedUpdates = GetAllUpdates("PC-01")
.Where(upd => upd.HResult != 0).ToList();

To get all timestamps of failed updates,

IList<DateTime> failedUpdates = GetAllUpdates("PC01")
.Where(upd => upd.HResult != 0)
.Select(upd => upd.Date).ToList();