locked
Searching for Installed Windows Updates RRS feed

  • Question

  • I would like to quickly query for Windows Updates that have already been installed.  I am currently using the Windows Update Agent API to do this but it takes entirely too long to run.  The query I am using within theIUpdateSearcher.Search (https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526(v=vs.85).aspx) method is as follows:

    "IsInstalled=1 and IsHidden=0"

    This query takes between 15 and 20 minutes to complete.  This is too long for my purposes.  I have not been able to find any useful information as to how I can speed this up or another alternative.

    I have noticed that "Control Panel -> Programs -> Programs and Features -> Installed Updates"  is able to get a list of all the installed updates within several seconds.  This was done on Windows 7 SP1 64 bit.

    In conclusion, is there a way I can speed up IUpdateSearcher.Search to get a list of installed updated?  Alternatively, do you know how Microsoft is getting the list of installed updated in "Control Panel -> Programs -> Programs and Features -> Installed Updates"?

    Also, the code I am using to perform my query can be found at (I am only performing the search portion so stop at approximately line 22):

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa387102(v=vs.85).aspx

    Thursday, December 17, 2015 12:37 PM

Answers

  • The following C++ console program will produce the same list of installed updates that is displayed by Control Panel | Programs and Feature | View Installed Updates.  It requires Win 7 or higher and was coded for a 32 bit system.

    // ListInstallCon.cpp : Defines the entry point for the console application.
    //
    #define NTDDI_VERSION NTDDI_WIN7
    #define _WIN32_WINNT _WIN32_WINNT_WIN7
    
    #include "sdkddkver.h"
    #include <iostream>
    #include <ShlObj.h>
    #include <atlbase.h>
    using namespace std;
    
    
    int main()
    {
    	HRESULT hr = CoInitialize(NULL);
    	int count = 0;
    
    	if (SUCCEEDED(hr))
    	{
    		CComPtr<IShellItem> pUpdates;
    		CComPtr<IEnumShellItems> pShellEnum;
    
    		hr = SHGetKnownFolderItem(FOLDERID_AppUpdates, static_cast<KNOWN_FOLDER_FLAG>(0), nullptr, IID_PPV_ARGS(&pUpdates));
    		hr = pUpdates->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&pShellEnum));
    		if (pShellEnum)
    		{
    			do {
    				CComPtr<IShellItem> pItem;
    				CComHeapPtr<WCHAR> szName;
    
    				hr = pShellEnum->Next(1, &pItem, nullptr);
    				if (pItem)
    				{
    					HRESULT hres = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, &szName);
    					wcout << static_cast<LPWSTR>(szName) << endl;
    					count++;
    				}
    			} while (hr == S_OK);
    		}
    	}
    	CoUninitialize();
    	wcout << L"Found " << count << " updates" << endl;
    	return 0;
    }
    
    

    Sunday, December 27, 2015 4:38 PM
  • Following is the code for a simple Windows Form app in VB that uses the Windows Update API to retrieve the list of updates already installed on a system from the Windows Update history.  It runs very quickly. The code assumes that nothing has been uninstalled. The Operation property of the History Entry can be checked to determine if the entry is for an installation or an uninstallation.

    The related form has a ListView and two buttons. When the "Get Updates" button is clicked the ListView is loaded with the update information.  The project includes a reference to the WUApiLib (COM). There is no error checking in the sample code.  Following are a screenshot of the App and the code behind the VB Form.

    VB Code -

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Close()
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim vbSess As New UpdateSession
            Dim vbSearch As IUpdateSearcher
            Dim histCollection As IUpdateHistoryEntryCollection
            Dim histCount As Integer
            Dim histEntry As IUpdateHistoryEntry
            Dim LvItem As ListViewItem
    
    
            vbSearch = vbSess.CreateUpdateSearcher()
    
            histCount = vbSearch.GetTotalHistoryCount()
    
            histCollection = vbSearch.QueryHistory(0, histCount)
    
            For Each histEntry In histCollection
                LvItem = ListView1.Items.Add(histEntry.Title)
                LvItem.SubItems.Add(histEntry.Date.ToString())
            Next
    
            ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
    
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Title.Width = 631
            InstallDate.Width = 93
        End Sub
    End Class



    • Edited by RLWA32 Friday, December 25, 2015 3:14 PM Expanded explanation
    • Proposed as answer by May Wang - MSFT Tuesday, December 29, 2015 2:47 AM
    • Marked as answer by May Wang - MSFT Tuesday, December 29, 2015 2:48 AM
    Friday, December 25, 2015 11:41 AM

All replies

  • #include "stdafx.h"
    #include "WUAtest.h"
    #include <Windows.h>
    #include <iostream>
    #include <atlbase.h>
    #include <Wuapi.h>
    #include <wuerror.h>
    #include <list>
    #include <fstream>
    #include <atlbase.h>
    #include <atlstr.h>
    #include <comutil.h>
    #include <MsXml.h>
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    
    
    CWinApp theApp;
    
    using namespace std;
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
    	int nRetCode = 0;
    
    	HMODULE hModule = ::GetModuleHandle(NULL);
    
    	if (hModule != NULL)
    	{
    		
    		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
    		{
    			// TODO: 
    			_tprintf(_T("\n"));
    			nRetCode = 1;
    		}
    		else
    		{
    			// TODO: 
    			try
    				{
    				HRESULT hr;
    				hr = CoInitialize(NULL);
    				char * filename="test.txt";
    				IUpdateSession* iUpdate;
    				IUpdateSearcher* searcher;
    				ISearchResult* results;
    
    				
    				BSTR criteria = SysAllocString(L"IsInstalled=1 "); // and IsHidden=0 or IsPresent=1
    
    				hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&iUpdate);
    				hr = iUpdate->CreateUpdateSearcher(&searcher);
    				hr = searcher->put_ServerSelection(ssWindowsUpdate);
    				hr = searcher->put_CanAutomaticallyUpgradeService(VARIANT_TRUE);
    				hr = searcher->put_IncludePotentiallySupersededUpdates(VARIANT_TRUE);
    				hr = searcher->put_Online(VARIANT_TRUE);
    
    
    				wcout << L"Searching for updates ..."<<endl;
    				hr = searcher->Search(criteria, &results); 
    				SysFreeString(criteria);
    
    				switch(hr)
    				{
    				case S_OK:
    					wcout<<L"List of applicable items on the machine:"<<endl;
    					break;
    				case WU_E_LEGACYSERVER:
    					wcout<<L"No server selection enabled"<<endl;
    					return 0;
    				case WU_E_INVALID_CRITERIA:
    					wcout<<L"Invalid search criteria"<<endl;
    					return 0;
    				}
    
    				IUpdateCollection *updateList;
    				IUpdate *updateItem;
    				LONG updateSize;
    				LONG totalKB=0;
    				results->get_Updates(&updateList);
    				updateList->get_Count(&updateSize);
    
    				if (updateSize == 0)
    				{
    					wcout << L"No updates found"<<endl;
    				}
    
    				ofstream outputFile;
    				outputFile.open(filename,ios::out);
    
    				for (LONG i = 0; i < updateSize; i++)
    				{
    					IStringCollection *KBCollection;
    					BSTR updateName;
    					LONG KBCount;
    					updateList->get_Item(i,&updateItem);
    
    
    					updateList->get_Item(i, &updateItem);
    					updateItem->get_Title(&updateName); 					USES_CONVERSION;
    					outputFile << W2A(CString(updateName)) << " --- ";
    					updateItem->get_KBArticleIDs(&KBCollection);
    					KBCollection->get_Count(&KBCount);
    					for(int i=0;i<KBCount;i++)
    					{
    						BSTR KBValue;
    						totalKB += 1;
    						KBCollection->get_Item(i,&KBValue);
    						USES_CONVERSION;
    						outputFile <<  W2A(CString("KB")) << W2A(CString(KBValue)) << endl; 
    					}
    
    					IUpdateCollection *updtCollection;
    					LONG updtBundledCount;        
    		
    					
    					outputFile << W2A(CString("\t Bundled Updates : "));
    					updateItem->get_BundledUpdates(&updtCollection);
    					hr = updtCollection->get_Count(&updtBundledCount);
    					if ((updtBundledCount>0) && (hr ==S_OK))
    					{
    						wcout << L"Bundled Updates " <<(updtBundledCount) << endl;
    						for(LONG j=0;j<updtBundledCount;j++)
    						{
    							IUpdate *bundledUpdateItem;
                    
    							updtCollection->get_Item(j,&bundledUpdateItem);   
    				
    							bundledUpdateItem->get_Title(&updateName);
    							USES_CONVERSION;
    							outputFile <<  W2A(CString("\t")) << W2A(CString(updateName)) << " - ";
                    
    							updateItem->get_KBArticleIDs(&KBCollection);
    							KBCollection->get_Count(&KBCount);
    							for(int i=0;i<KBCount;i++)
    							{
    								BSTR KBValue;
    								totalKB += 1;
    								KBCollection->get_Item(i,&KBValue);
    								outputFile <<  W2A(CString("KB")) << W2A(CString(KBValue)) << endl;
    							}
    							//wcout<<L"    "<<j+1<<" - "<<bundledName<<endl;
    						}
    
    					}
    				}
    				wcout << "Total KBs : " << totalKB << endl;
    				outputFile.close();
    				::CoUninitialize();
    				}
    			catch( const std::exception & ex )
    				{
    					cout << ex.what();
    					::CoUninitialize();
    				}
    
    
    		}
    	}
    	else
    	{
    		
    		_tprintf(_T(" GetModuleHandle \n"));
    		nRetCode = 1;
    	}
    
    
    
    	return nRetCode;
    }
    

    Hi,

    Thanks for posting here.

    Is your project a C++ project or VB project? Because the code link you provided is related VB. I use C++ code to do a test about the IUpdateSearcher, the searching time is about 7000ms. 

    This function is to search updates on a server, so I think the time cost is not only refer to the code , the machine but also with the Internet.

    May


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, December 18, 2015 9:42 AM
  • Thank you for your reply.  The project is using VB, but I was not sure where to place this question as non of the categories really seemed to apply.  As for your code, that is pretty much what I am doing in VB.  I find it hard to believe your code completes in 7 seconds, unless there is a very minimal number of updates installed on the machine.
    Wednesday, December 23, 2015 6:22 PM
  • Hi,

    Thanks for your feedback.

    I am sorry that VB issue is not proper in C++ forum.

    >As for your code, that is pretty much what I am doing in VB.  I find it hard to believe your code completes in 7 seconds, unless there is a very minimal number of updates installed on the machine.

    Yes, the number of updates installed on the machine will have affection on the time.

    I think you may have a try to let your VB call C++ dll to test if the time will have difference with your original.

    And you may ask VB question from here for better support.

    May


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, December 25, 2015 3:25 AM
  • Following is the code for a simple Windows Form app in VB that uses the Windows Update API to retrieve the list of updates already installed on a system from the Windows Update history.  It runs very quickly. The code assumes that nothing has been uninstalled. The Operation property of the History Entry can be checked to determine if the entry is for an installation or an uninstallation.

    The related form has a ListView and two buttons. When the "Get Updates" button is clicked the ListView is loaded with the update information.  The project includes a reference to the WUApiLib (COM). There is no error checking in the sample code.  Following are a screenshot of the App and the code behind the VB Form.

    VB Code -

    Public Class Form1
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Close()
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim vbSess As New UpdateSession
            Dim vbSearch As IUpdateSearcher
            Dim histCollection As IUpdateHistoryEntryCollection
            Dim histCount As Integer
            Dim histEntry As IUpdateHistoryEntry
            Dim LvItem As ListViewItem
    
    
            vbSearch = vbSess.CreateUpdateSearcher()
    
            histCount = vbSearch.GetTotalHistoryCount()
    
            histCollection = vbSearch.QueryHistory(0, histCount)
    
            For Each histEntry In histCollection
                LvItem = ListView1.Items.Add(histEntry.Title)
                LvItem.SubItems.Add(histEntry.Date.ToString())
            Next
    
            ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
    
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Title.Width = 631
            InstallDate.Width = 93
        End Sub
    End Class



    • Edited by RLWA32 Friday, December 25, 2015 3:14 PM Expanded explanation
    • Proposed as answer by May Wang - MSFT Tuesday, December 29, 2015 2:47 AM
    • Marked as answer by May Wang - MSFT Tuesday, December 29, 2015 2:48 AM
    Friday, December 25, 2015 11:41 AM
  • The following C++ console program will produce the same list of installed updates that is displayed by Control Panel | Programs and Feature | View Installed Updates.  It requires Win 7 or higher and was coded for a 32 bit system.

    // ListInstallCon.cpp : Defines the entry point for the console application.
    //
    #define NTDDI_VERSION NTDDI_WIN7
    #define _WIN32_WINNT _WIN32_WINNT_WIN7
    
    #include "sdkddkver.h"
    #include <iostream>
    #include <ShlObj.h>
    #include <atlbase.h>
    using namespace std;
    
    
    int main()
    {
    	HRESULT hr = CoInitialize(NULL);
    	int count = 0;
    
    	if (SUCCEEDED(hr))
    	{
    		CComPtr<IShellItem> pUpdates;
    		CComPtr<IEnumShellItems> pShellEnum;
    
    		hr = SHGetKnownFolderItem(FOLDERID_AppUpdates, static_cast<KNOWN_FOLDER_FLAG>(0), nullptr, IID_PPV_ARGS(&pUpdates));
    		hr = pUpdates->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&pShellEnum));
    		if (pShellEnum)
    		{
    			do {
    				CComPtr<IShellItem> pItem;
    				CComHeapPtr<WCHAR> szName;
    
    				hr = pShellEnum->Next(1, &pItem, nullptr);
    				if (pItem)
    				{
    					HRESULT hres = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, &szName);
    					wcout << static_cast<LPWSTR>(szName) << endl;
    					count++;
    				}
    			} while (hr == S_OK);
    		}
    	}
    	CoUninitialize();
    	wcout << L"Found " << count << " updates" << endl;
    	return 0;
    }
    
    

    Sunday, December 27, 2015 4:38 PM