简体   繁体   中英

Is there a way to access the date of the last installed Windows update using C#?

I'm working on adapting part of a powershell script to a WPF C# windows service and I'm having trouble finding a way to get the last installed windows update like the following script does. I need a way to check that a workstation has installed windows updates in the last 30 days, if it has it passes the audit if not it fails the audit.

$today = Get-Date   
$session = (New-Object -ComObject 'Microsoft.Update.Session')
$lastUpdate = ($session.QueryHistory("",0,100) | Where-Object 
{$_.ResultCode -eq 2}).Date | Sort-Object -descending
$compInfo = Get-ComputerInfo | select WindowsProductName, WindowsVersion
$OSversion = ($compInfo.WindowsProductName + " " + $compInfo.WindowsVersion).replace("Windows ","")

if (!$lastUpdate) {
    $never = $true
    $lastUpdate = Get-Date -Year 0001 -Month 01 -Day 01 -Hour 00 -Minute 00 -Second 00
} else {
    # Convert from UTC to timezone
    $timediff = [int]((Get-Timezone | select BaseUtcOffset | Out-String -Stream)[3][2].ToString())
    $lastUpdate = $lastUpdate[0].AddHours(-$timediff)
}

# Audit result
if ($never -or [datetime]$lastUpdate -le ($today.AddDays(-30))) {
    # Audit fail
    $pass = $false
    if ($never) {
        $lastUpdate = "Never"
    } else {
        $lastUpdate = [string]$lastUpdate
    }
} else {
    # Audit pass
}

I've been reading about the WUApi, but have not been able to get any variation of the following code to not produce errors when trying to use the IUpdateSearcher class. I've tried different namespaces an such and still haven't had any luck.

using WUApiLib;

UpdateSessionClass uSession = new UpdateSessionClass();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
ISearchResult uResult = uSearcher.Search("IsInstalled=0 and Type='Software'");

It doesn't seem like I will be able to replicate the powershell version exactly, but from what I've researched on the WUAPI I could just check if the computer has any updates that haven't been installed and assign a pass if there are no updates and fail if there are.

As explained here , you could compile a file summarizing the updates.

By default, it is written to your desktop:

"%USERPROFILE%\Desktop\WindowsUpdate.log"

This operation can be started as sub-process of your skript or program. It does not require admininstrative privileges.

You can then read the file up to something like the following:

2022.07.06 16:56:05.5197996 3936  8528  UDP               Title = Security Intelligence-Update f??r Microsoft Defender Antivirus - KB2267602 (Version 1.369.880.0)
2022.07.06 16:56:05.5198024 3936  8528  UDP               UpdateId = B701392C-6DF8-4D94-A07F-35D06C5014E7.200

The Title lines contain datei/time and KB number of the updates in chronological order.

In comments, you mentioned these (compile-time) errors:

The type or namespace name 'UpdateSessionClass' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'IUpdateSearcher' could not be found (are you missing a using directive or an assembly reference?)

Apparently, something was wrong with your reference to WUApi.dll. This should work:

  1. In the solution explorer, directly below the project name, right-click on Dependencies. A context menu appears.
  2. From the context menu, select "Add COM Reference..." The "Reference Manager" dialog appears.
  3. In the dialog, on tab COM, scroll down and check "WUAPI 2.0 Type Library". Click OK.

Now you can query the update history, just like you did in the PowerShell script.

using WUApiLib;

IUpdateSession3 session = new UpdateSession();
IUpdateHistoryEntryCollection history = session.QueryHistory("", 0, 1);
if (history.Count > 0)
{
    Console.WriteLine($"Latest Windows update was on {history[0].Date}.");
}
else
{
    Console.WriteLine("Not a single Windows update found.");
}

Notice I am not sorting history myself. From the documentation (emphasis mine):

[out] retval A pointer to an IUpdateHistoryEntryCollection interface that contains the matching event records on the computer in descending chronological order.

In other words, the top row is the latest update. Hence these parameters when calling IUpdateSession3::QueryHistory :

  • "" : no search criteria, just get me anything
  • 0 : start from the top
  • 1 : give me (at most) one row

Please keep in mind that the history may yield updates that have been rolled back afterwards. I don't know how to fix this. But I assume your PowerShell script was having the same issue.

Kudos to the useful code sample in this post: https://stackoverflow.com/a/815525/2485966

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM