Windows update offline using path from Windows Update Catalog

Quang Nguyen 20 Reputation points
2023-09-28T05:41:02.53+00:00

I face some challenge when development application support update windows offline by using Windows Update Agent API and update packet (.msu, .cab) download from Windows Update Catalog.

  1. After I scan WSUSSCAN.cab for detect list of applicable items on the machine, how I can use WUA API to add manual downloaded packet for update installer. I try to using IUpdate2::CopyToCache but it always make kill applicatiom.
  2. Can I add manual downloaded packet to C:/Windows/SoftwareDistribution/Download folder and using WUA API for update? If yes, how I can do it ?

Thank you.

Windows development Windows API - Win32
Windows for business Windows Client for IT Pros User experience Other
{count} votes

Accepted answer
  1. RLWA32 49,536 Reputation points
    2023-10-08T12:31:15.2+00:00

    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

    OfflineUpdate

    Expanded .msu

    ExpandedMsu

    Installed updates

    InstalledUpdates


1 additional answer

Sort by: Most helpful
  1. Limitless Technology 44,751 Reputation points
    2023-09-28T11:17:50.39+00:00
    Hello ,
    
    Offline update can be somehow tricky, but overall if a straight forward process. I can recommend the next:
    
    1.Before adding update packages to the system, ensure they are properly prepared. This may involve extracting files from CAB files or ensuring that MSU files are intact.
    2.Copy the update packages to a folder on the target machine where you plan to store them. The C:/Windows/SoftwareDistribution/Download folder is commonly used for this purpose.
    3.You can use the WUA API to programmatically install updates from the copied update packages. Here are the general steps using C#:
    
    using System;
    using WUApiLib; // Reference the Windows Update Agent API COM library
    
    // Create an instance of the UpdateSession
    UpdateSession updateSession = new UpdateSession();
    
    // Create an instance of the UpdateSearcher
    IUpdateSearcher updateSearcher = updateSession.CreateUpdateSearcher();
    
    // Set the search criteria
    updateSearcher.Online = false; // Offline mode
    updateSearcher.ServerSelection = ServerSelection.ssWindowsUpdate;
    
    // Optionally, you can specify a specific folder containing the update packages
    // updateSearcher.ServiceID = "<Path to the folder>";
    
    // Search for available updates
    ISearchResult searchResult = updateSearcher.Search("IsInstalled=0");
    
    // Install the updates
    IUpdateCollection updatesToInstall = new UpdateCollection();
    foreach (IUpdate update in searchResult.Updates)
    {
        updatesToInstall.Add(update);
    }
    
    if (updatesToInstall.Count > 0)
    {
        // Create an UpdateInstaller
        UpdateInstaller installer = updateSession.CreateUpdateInstaller();
        installer.Updates = updatesToInstall;
    
        // Perform the installation
        installer.Install();
    }
    
    This code performs an offline search for updates not yet installed on the system and installs them from the specified folder (or the default C:/Windows/SoftwareDistribution/Download folder if no path is specified).
    
    
    --If the reply is helpful, please Upvote and Accept as answer--
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.