Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows Update API with C# not finding any installed updates

I have a number of Windows 7 PC's that need patching with a particular Windows update, using the Windows Update API in a C# console app. The API needs to search the installed updates and report back if it's already installed and perform the installation if not.

Whilst testing on a Virtual PC (Windows 7 Professional Hyper-v client) I have a situation similar to the target PCs (Windows 7 Embedded) where the following code returns (very quickly and without any exceptions) 0 updates. Which I know to be wrong. In fact, it even returns this after I install a .msu update.

Code:

 UpdateSession uSession = new UpdateSession();
 IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
 uSearcher.Online = false;
 try
 {
    ISearchResult sResult = uSearcher.Search("IsInstalled=1 And IsHidden=0");
    Console.WriteLine("Found " + sResult.Updates.Count + " updates");
    foreach (IUpdate update in sResult.Updates)
    {
       Console.WriteLine(update.Title);
       if (update.Title.ToLower().Contains("kb123456")) {
        //Update is not required
        ReportInstalled();
        return;
       }
     }
     //If we get here, the update is not installed
     InstallUpdate();
  }
  catch (Exception ex)
  {
    Console.WriteLine("Something went wrong: " + ex.Message);
  }

Now for the fun part. If I open up the Windows Update from the Control Panel and click 'Check for updates', it goes off for a while and comes back with a bunch of updates to install. At this point, if I run the above code, it works as expected and reports over 200 installed updates.

It appears that the manual process of searching for updates starts/restarts some services and/or other processes, however, I am struggling to figure out exactly what I need to do to the system to get it into the correct state. I expect the answer will be a simple case of starting service x or process y with a set of args, but which?

Some (not all) of the things I have tried but did not alter the behavior:

  1. Started the BITS service, restarted Windows Update Service
  2. Tried launching wuauclt.exe with various switches (documented here in the comments)

With the machine in a state where the code runs correctly (after I run WU manually), I have noticed the process wuauclt.exe appears to start when the above code is run. When it's in the target state (before I run WU manually), wuauclt.exe does not start, and I am not able to launch this manually, I suspect this is a big clue.

One other clue is the state of Windows Update before I run it manually. In the control panel windows update looks like this:

enter image description here

After running WU and installing updates via that method, and the machine is in a state where the code runs as expected WU looks like:

enter image description here

To sum up, I need this process to automate the installation of an update. If I detect 0 installed updates, I know the machine is in a particular state, so I will need to launch/restart some processes and services (programmatically) to get the machine into the correct state before running my code. Knowing what to run/restart is the essence of this problem.

like image 200
SausageFingers Avatar asked Oct 29 '22 05:10

SausageFingers


1 Answers

Since this question doesn't have an answer at the moment (though the combined comments mostly give the answer), here's what happened here:

This is a very common approach to checking "Do I have the patches I need in order for my program to run successfully?", and it has a short-term and a long-term problem.

The short-term problem:

The search code is doing an offline scan (it's setting IUpdateSearcher::Online to false). That's a common tactic to speed up the search. The problem is that it only processes updates that were available during the previous online scan. If the computer hasn't done an online scan in ages, then the results will be stale. If the computer has had a notable hardware or software configuration change since the last online scan, then the results will be incomplete. If the computer has never done an online scan, then IUpdateSearcher::Search won't return an error -- it will just instantly report that no updates are applicable.

So if you want to try to speed things up by doing an offline scan, it's good coding practice to check IAutomaticResults::LastSearchSuccessDate, which tells you the last time that Automatic Updates did a scan. Since Automatic Updates scans are online, you know that an online scan occurred at that time. If the date is more than a few days old, you should do an online scan instead.

The long-term problem:

This code is assuming that an update KB123456 exists and is relevant to the computer. But that's inherently a time-limited assumption, and the time limit these days is often quite short. If the patches in KB123456 get rolled into a newer cumulative update KB234567, then at some point KB123456 may be expired from Windows Update. At that point your search will always return "not installed", even if the patched code is actually on the PC.

Conclusion

Instead of trying to check "Is KB X installed?", the better approach is to test "Is the fix/feature I need installed?"

  • If at all possible, check for the fix/feature directly. For example, if you need a particular new Windows API, then you can use API Sets if the OS supports it, or just use LoadLibrary and GetProcAddress to see if the DLL you're after contains the function you need.

  • If testing directly for the fix/feature isn't possible, then test the state of Windows itself to see if it's what you need. In Windows 10, you often just need to check the build number. On older versions of the OS, instead of hardcoding a KB number, you can look at the patch notes for the KB, find out what DLLs it updates and what version numbers it updates them to, then use those DLL names and versions for your runtime check.

  • Depending on exactly what you're testing and the context in which you're doing the test (scripting, etc.), the DISM command may also be helpful -- if you need a particular Windows package to be present, you can use DISM /ONLINE /GET-PACKAGES and see if your package shows up in the output.

like image 151
Mark Phaedrus Avatar answered Nov 10 '22 20:11

Mark Phaedrus