I was able to use the Windows Update API to do an offline install of KB5030211. First I uninstalled the update from a Win 10 22H2 test system. It should be noted that Cumulative updates like this one also contain a servicing stack update. Servicing stack updates cannot be uninstalled. After rebooting, I downloaded the related .msu standalone update from the Microsoft Catalog. Then I expanded the contents of the .msu file to a folder using the expand command. An example of its use is in Description of the Windows Update Standalone Installer in Windows. I also downloaded the standalone wsusscn2.cab file for offline scanning.
The offline scan detected the update was not installed, but at first I was unable to successfully use the IUpdate2::CopyToCache method. Then I realized that the IUpdate interface returned from the search may be for a bundled update. which was causing the failure of the call to CopyToCache. This turned out to be the case in my test and after obtaining an IUpdate2 interface for the update in the bundle the CopyToCache method succeeded.
The IUpdateInstaller::Install method was able to use the cached update information to successfully install the update.
The code that I used in my test is as follows -
#include <wuapi.h>
#include <comdef.h>
#include <iostream>
using namespace _com_util;
_COM_SMARTPTR_TYPEDEF(IUpdateSession3, IID_IUpdateSession3);
_COM_SMARTPTR_TYPEDEF(IUpdateServiceManager2, IID_IUpdateServiceManager2);
_COM_SMARTPTR_TYPEDEF(IUpdateServiceCollection, IID_IUpdateServiceCollection);
_COM_SMARTPTR_TYPEDEF(IUpdateService, IID_IUpdateService);
_COM_SMARTPTR_TYPEDEF(IUpdateSearcher, IID_IUpdateSearcher);
_COM_SMARTPTR_TYPEDEF(ISearchResult, IID_ISearchResult);
_COM_SMARTPTR_TYPEDEF(IUpdateCollection, IID_IUpdateCollection);
_COM_SMARTPTR_TYPEDEF(IUpdate, IID_IUpdate);
_COM_SMARTPTR_TYPEDEF(IUpdate2, IID_IUpdate2);
_COM_SMARTPTR_TYPEDEF(IUpdateInstaller, IID_IUpdateInstaller);
_COM_SMARTPTR_TYPEDEF(IInstallationResult, IID_IInstallationResult);
_COM_SMARTPTR_TYPEDEF(IStringCollection, IID_IStringCollection);
void DoInstall(IUpdateCollection* puc);
int main()
{
auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
try
{
IUpdateSession3Ptr pus(L"Microsoft.Update.Session");
IUpdateServiceManager2Ptr pmgr(L"Microsoft.Update.ServiceManager");
IUpdateServicePtr pCabSvc;
_bstr_t name(L"Offline Synch Service");
_bstr_t scancab(L"C:\\Users\\RLWA32\\Downloads\\wsusscn2.cab");
//_bstr_t scancab(L"C:\\Users\\RLWA32\\Downloads\\5030211\\wsusscan.cab");
_bstr_t pkgpath(L"C:\\Users\\RLWA32\\Downloads\\5030211\\Windows10.0-KB5030211-x64.cab");
_bstr_t offlineServiceID;
CheckError(pmgr->AddScanPackageService(name, scancab, 0, &pCabSvc));
CheckError(pCabSvc->get_ServiceID(offlineServiceID.GetAddress()));
try
{
_bstr_t criteria(L"IsInstalled=0 and UpdateID='4aec4d66-a06c-4544-9f79-55ace822e015'"); // and UpdateID='4aec4d66-a06c-4544-9f79-55ace822e015'
IUpdateSearcherPtr psrch;
CheckError(pus->CreateUpdateSearcher(&psrch));
CheckError(psrch->put_ServerSelection(ssOthers));
CheckError(psrch->put_ServiceID(offlineServiceID));
ISearchResultPtr pres;
CheckError(psrch->Search(criteria, &pres));
OperationResultCode orc{};
CheckError(pres->get_ResultCode(&orc));
if (orc == orcSucceeded)
{
IUpdateCollectionPtr pcoll;
CheckError(pres->get_Updates(&pcoll));
long updatecount{};
CheckError(pcoll->get_Count(&updatecount));
if (updatecount == 1)
{
IUpdatePtr pupdate;
_bstr_t title;
char c;
CheckError(pcoll->get_Item(0, &pupdate));
CheckError(pupdate->get_Title(title.GetAddress()));
IUpdateCollectionPtr pBundled;
hr = pupdate->get_BundledUpdates(&pBundled);
if (SUCCEEDED(hr) && pBundled)
{
long nBundled{};
CheckError(pBundled->get_Count(&nBundled));
if (nBundled != 1)
{
std::cout << "Bundled update contained " << nBundled << " updates, 1 expected" << std::endl;
_com_raise_error(E_UNEXPECTED);
}
IUpdatePtr pbu;
CheckError(pBundled->get_Item(0, &pbu));
pupdate = pbu;
}
IUpdate2Ptr pu2 = pupdate;
std::cout << "Title: " << (char*)title << std::endl;
std::cout << "Install Update? (y/n)" << std::endl;
std::cin >> c;
if (c == 'Y' || c == 'y')
{
IStringCollectionPtr pstrings(L"Microsoft.Update.StringColl");
long ndx{};
CheckError(pstrings->Add(pkgpath, &ndx));
std::cout << "Copying to Windows Update cache" << std::endl;
CheckError(pu2->CopyToCache(pstrings));
std::cout << "Performing the installation" << std::endl;
DoInstall(pcoll);
}
}
else
std::cout << "Update count was " << updatecount << ", expected 1" << std::endl;
}
else
std::cout << "Operation result code was " << orc << std::endl;
}
catch (const _com_error& ce)
{
std::cout << "COM error was 0x" << std::hex << ce.Error() << std::endl;
}
}
catch (const _com_error& ce)
{
std::cout << "COM error was 0x" << std::hex << ce.Error() << std::endl;
}
}
CoUninitialize();
return 0;
}
void DoInstall(IUpdateCollection* puc)
{
try
{
IUpdateInstallerPtr pInstaller(L"Microsoft.Update.Installer");
IInstallationResultPtr presult;
OperationResultCode orc{};
CheckError(pInstaller->put_Updates(puc));
CheckError(pInstaller->Install(&presult));
CheckError(presult->get_ResultCode(&orc));
if (orc == orcSucceeded)
{
VARIANT_BOOL vRebootRequired{ VARIANT_FALSE };
CheckError(presult->get_RebootRequired(&vRebootRequired));
std::cout << "Install succeeded, reboot is "
<< (vRebootRequired == VARIANT_TRUE ? "required" : "not required") << std::endl;
}
else
{
long hr{};
CheckError(presult->get_HResult(&hr));
std::cout << "Install did not succeed, result code was " << orc
<< " and exception raised during installation was 0x" << std::hex << hr << std::endl;
}
}
catch (const _com_error& ce)
{
std::cout << "Update installation COM error was 0x" << std::hex << ce.Error() << std::endl;
}
}
This is a sample for demonstration purposes that does not include comprehensive error checking, handle multiple updates or otherwise do any prerequisite testing. It has not been tested on a system that does not have the related servicing stack update installed.
Successful install
Expanded .msu
Installed updates