Search code examples
c#registry

C# - Get Last Windows-Update via Remote Registry - OpenSubKey("WindowsUpdate") returns NULL


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.


Solution

  • 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();