我正在使用 WUAPI 搜索、下载和安装 Windows 更新(目前未使用 WSUS 服务器)。我正在使用具有特殊映像(Windows 10)的 VM 对其进行测试,该映像缺少多个更新(目前为 8 个)。我以管理员身份启动程序并提升权限以避免出现问题。它搜索更新,列出它们,下载它们并安装它们而不会出现任何错误。在我的情况下完成后,不需要重新启动。(出于测试目的,我内置_maxDownloadAndInstallSize = 1
因为累积更新和功能更新需要很长时间才能安装:/) 但是当我现在打开我的 Windows 更新对话框时,它仍然显示它想要更新。此外,已安装的更新不会出现在历史记录中。我试图重新启动,但这也无济于事。当我再次执行我的程序时,它会毫无问题地安装下一个更新。情况是一样的。系统无法识别其中的任何内容。我也尝试过一次安装。它没有改变任何东西。
如果我的概念是错误的,或者我有什么有趣的许可问题,你知道吗?
对我来说,“完成”已安装的更新似乎缺少一些东西,所以一切都得到了同步。
我的代码(对不起,这只是实验性代码,因为我有问题,一些检查已经完成了好几次......)
(程序.cs)
static void Main(string[] args)
{
try
{
WindowsUpdateAPI updater = new WindowsUpdateAPI();
IUpdateInstaller2 installer = new UpdateInstaller();
updater.ExecuteWindowsUpdate(installer);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Exception occured");
}
}
(WindowsUpdateAPI.cs)
public class WindowsUpdateAPI
{
enum LogType
{
info,
success,
warning,
error,
fatalError
}
static int _maxDownloadAndInstallSize = 1; // 0 => all
public void ExecuteWindowsUpdate(IUpdateInstaller2 updateInstaller)
{
//IUpdateInstaller2 Installs or uninstalls updates from or onto a computer.
updateInstaller = new UpdateInstaller();
LogMessage("Automatic Windows Update mechanism started", LogType.info);
LogMessage("\n-- Basic Checks --", LogType.info);
ISystemInformation info = new SystemInformation();
if (updateInstaller.RebootRequiredBeforeInstallation || CheckRegistriesForPendingRestart())
{
if (!info.RebootRequired)
{
LogMessage("Reboot not required by system", LogType.warning);
}
//TODO:Reboot PC
LogMessage("Reboot required", LogType.warning);
Console.ReadLine();
return;
}
else
{
LogMessage("No reboot required", LogType.info);
}
if (info.RebootRequired)
{
LogMessage("Reboot required by system", LogType.warning);
}
if (updateInstaller.IsBusy)
{
LogMessage("Ongoing updates found", LogType.warning);
}
else
{
LogMessage("No ongoing updates found", LogType.info);
UpdateSession updateSession = new UpdateSession();
ISearchResult updateResult = SearchWindowsUpdates(updateSession);
DownloadWindowsUpdates(updateSession, updateResult, false);
bool rebootRequired = false;
InstallWindowsUpdates(updateInstaller, updateResult, rebootRequired);
if (rebootRequired)
{
LogMessage("Reboot required by update", LogType.warning);
}
}
LogMessage("\n -- End --", LogType.info);
Console.ReadLine();
}
private static void LogMessage(string message, LogType logType)
{
switch (logType)
{
case LogType.info:
Console.ForegroundColor = ConsoleColor.White;
break;
case LogType.success:
Console.ForegroundColor = ConsoleColor.Green;
break;
case LogType.warning:
Console.ForegroundColor = ConsoleColor.Yellow;
break;
case LogType.error:
Console.ForegroundColor = ConsoleColor.Red;
break;
case LogType.fatalError:
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
break;
}
Console.WriteLine(message);
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.White;
}
private static int GetNumberOfMaxUpdates(int updateCount)
{
return ( _maxDownloadAndInstallSize > 0 ? Math.Min(updateCount, _maxDownloadAndInstallSize) : updateCount);
}
private static ISearchResult SearchWindowsUpdates(UpdateSession updateSession)
{
LogMessage("\n-- Search --", LogType.info);
IUpdateSearcher updateSearcher = updateSession.CreateUpdateSearcher();
LogMessage("Searching for Windows Updates..", LogType.info);
//TODO: criteria: RebootRequired, IsHidden,
ISearchResult
updateSearchResult = updateSearcher.Search(
"IsInstalled=0"); //Use operators written with capital letters as example: AND Type='Software'
LogMessage($"Found {updateSearchResult.Updates.Count} available updates", LogType.info);
foreach (IUpdate update in updateSearchResult.Updates)
{
//TODO:Implement logging (network share)
LogMessage($"Update \"{update.Title}\" is available", LogType.info);
}
return updateSearchResult;
}
private static void DownloadWindowsUpdates(UpdateSession updateSession, ISearchResult updateSearchResult, bool forceDownloadAll)
{
LogMessage("\n-- Download --", LogType.info);
UpdateDownloader updateDownloader = updateSession.CreateUpdateDownloader();
if (updateDownloader.Updates == null)
{
updateDownloader.Updates = new UpdateCollection();
}
int updateCount = GetNumberOfMaxUpdates(updateSearchResult.Updates.Count);
LogMessage($"Number of max Updates to download is {updateCount}", LogType.info);
for (int i = 0; i < updateCount; i++)
{
IUpdate update = updateSearchResult.Updates[i];
if (!update.IsDownloaded || forceDownloadAll)
{
LogMessage($"Update \"{update.Title}\" prepared for download", LogType.info);
updateDownloader.Updates.Add(update);
}
}
LogMessage($"Downloading updates..", LogType.info);
if (updateDownloader.Updates.Count > 0)
{
IDownloadResult downloadResult = updateDownloader.Download();
for (int i = 0; i < updateDownloader.Updates.Count; i++)
{
PrintOperationResultCode("Download of Update", updateDownloader.Updates[0].Title, downloadResult.GetUpdateResult(i).ResultCode);
}
}
}
private static void PrepareUpdatesForInstall(UpdateCollection updatesToInstall, ISearchResult updateSearchResult, bool rebootRequiredBeforeUpdate)
{
int updateCount = GetNumberOfMaxUpdates(updateSearchResult.Updates.Count);
LogMessage($"Number of max Updates to install is {updateCount}", LogType.info);
for (int i = 0; i < updateCount; i++)
{
IUpdate update = updateSearchResult.Updates[i];
if (!update.IsDownloaded)
{
LogMessage($"Update \"{update.Title}\" cannot be prepared for install because it's not downloaded", LogType.error);
}
else if (update.IsInstalled)
{
LogMessage($"Update \"{update.Title}\" is already installed", LogType.info);
}
else
{
updatesToInstall.Add(update);
LogMessage($"Update \"{update.Title}\" prepared for install", LogType.info);
}
}
}
private static void InstallWindowsUpdates(IUpdateInstaller2 updateInstaller, ISearchResult updateSearchResult, bool rebootRequired)
{
LogMessage("\n-- Install --", LogType.info);
IInstallationResult updateInstallationResult;
//Update collection which is used for installing
UpdateCollection updatesToInstall = new UpdateCollection();
bool rebootRequiredBeforeInstall = false;
PrepareUpdatesForInstall(updatesToInstall, updateSearchResult, rebootRequiredBeforeInstall);
if (rebootRequiredBeforeInstall)
{
LogMessage($"A Reboot is required before updating", LogType.warning);
return;
}
//Assign updates to install
updateInstaller.Updates = updatesToInstall;
updateInstaller.ForceQuiet = true;
if (updateInstaller.RebootRequiredBeforeInstallation)
{
LogMessage($"A Reboot is required before updating", LogType.warning);
Console.ReadLine();
return;
}
if (updatesToInstall.Count > 0)
{
LogMessage($"Installing updates..", LogType.info);
updateInstallationResult = updateInstaller.Install();
rebootRequired = false;
for (int i = 0; i < updateInstaller.Updates.Count; i++)
{
if (updateInstaller.Updates[i].IsInstalled &&
updateInstaller.Updates[i].InstallationBehavior.RebootBehavior != InstallationRebootBehavior.irbNeverReboots)
{
rebootRequired = true;
}
PrintOperationResultCode("Install of Update", updateInstaller.Updates[i].Title, updateInstallationResult.GetUpdateResult(i).ResultCode);
}
}
}
private static void PrintOperationResultCode(string messagePrefix, string updateTitle, OperationResultCode resultCode)
{
switch (resultCode)
{
case OperationResultCode.orcSucceeded:
LogMessage($"{messagePrefix} \"{updateTitle}\" succeeded", LogType.success);
//TODO: Implement logging (network share)
break;
case OperationResultCode.orcSucceededWithErrors:
LogMessage($"{messagePrefix} \"{updateTitle}\" succeeded with errors", LogType.warning);
//TODO: Implement logging (network share)
break;
case OperationResultCode.orcFailed:
LogMessage($"{messagePrefix} \"{updateTitle}\" failed", LogType.error);
break;
case OperationResultCode.orcAborted:
LogMessage($"{messagePrefix} \"{updateTitle}\" aborted", LogType.fatalError);
break;
case OperationResultCode.orcInProgress:
LogMessage($"{messagePrefix} \"{updateTitle}\" in progress", LogType.warning);
break;
case OperationResultCode.orcNotStarted:
LogMessage($"{messagePrefix} \"{updateTitle}\" not started yet", LogType.warning);
break;
}
}
private static bool CheckRegistriesForPendingRestart()
{
using var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey RebootRequired = hklm.OpenSubKey(
@"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired", false);
RegistryKey RebootPending = hklm.OpenSubKey(
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending", false);
RegistryKey PackagesPending = hklm.OpenSubKey(
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\PackagesPending", false);
RegistryKey Pending = hklm.OpenSubKey(
@"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending", false);
//Here we are searching for a keys in the HKLM registry that are created when pending reboot
bool rebootNeededBecauseOfRegistryKey = false;
if (RebootRequired != null || RebootRequired != null || PackagesPending != null)
{
rebootNeededBecauseOfRegistryKey = true;
}
if (RebootRequired != null)
{
RebootRequired.Close();
}
if (RebootPending != null)
{
RebootPending.Close();
}
if (PackagesPending != null)
{
PackagesPending.Close();
}
if (rebootNeededBecauseOfRegistryKey)
{
return true;
}
//Key SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending exists always but it is empty if no pending reboot , if key contains any sub keys, only then reboot is needed.
if (Pending != null)
{
foreach (RegistryKey subkey in Pending.GetSubKeyNames()
.Select(keyName => Pending.OpenSubKey(keyName)))
{
Pending.Close();
return true;
}
Pending.Close();
}
Pending.Close();
return false;
}
}