The Road to Delphi

Delphi – Free Pascal – Oxygene


8 Comments

search for installed windows updates using Delphi, WMI and WUA

Sometimes we need determine if a particular windows hotfix or update is installed in the system. to do this task you can use two approaches

WMI (Windows Management Instrumentation)

using the Win32_QuickFixEngineering class, you can retrieve a small system-wide update, commonly referred to as a quick-fix engineering (QFE) update.

check this code which list the updates installed in the system

procedure  GetWin32_QuickFixEngineeringInfo;
const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_QuickFixEngineering','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Caption                        %s',[FWbemObject.Caption]));// String
    //Writeln(Format('CSName                         %s',[FWbemObject.CSName]));// String
    Writeln(Format('Description                    %s',[FWbemObject.Description]));// String
    Writeln(Format('FixComments                    %s',[FWbemObject.FixComments]));// String
    Writeln(Format('HotFixID                       %s',[FWbemObject.HotFixID]));// String
    Writeln(Format('InstallDate                    %s',[FWbemObject.InstallDate]));// Datetime
    Writeln(Format('InstalledBy                    %s',[FWbemObject.InstalledBy]));// String
    Writeln(Format('InstalledOn                    %s',[FWbemObject.InstalledOn]));// String
    Writeln(Format('Name                           %s',[FWbemObject.Name]));// String
    Writeln(Format('ServicePackInEffect            %s',[FWbemObject.ServicePackInEffect]));// String
    Writeln(Format('Status                         %s',[FWbemObject.Status]));// String
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

Now to find if a particular update is installed you can check the HotFixID property value (which is the Unique identifier associated with a particular update) and write a function like this

//use in this way ISHotFixID_Installed('KB982799')
function  ISHotFixID_Installed(const HotFixID : string): Boolean;
const
  wbemFlagForwardOnly = $00000020;
  wbemFlagReturnImmediately = $00000010;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT * FROM Win32_QuickFixEngineering Where HotFixID="%s"',[HotFixID]),'WQL',wbemFlagForwardOnly OR wbemFlagReturnImmediately);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  Result:= oEnum.Next(1, FWbemObject, iValue) = 0;
end;

Ok this is fine, but Starting with Windows Vista, the Win32_QuickFixEngineering class returns only the updates supplied by Component Based Servicing (CBS), so some updates are not listed.

WUA (Windows Update Agent)

using the Windows Update Agent API is a best option to retrieve the list of updates, you can access the interfaces and objects from this API from delphi importing the wuapi.dll file or creating a late-binding com object using the Microsoft.Update.Session GUID. the next samples uses the late-binding way.

from the MSDN site :

The Windows Update Agent (WUA) API is a set of COM interfaces that enable system administrators and programmers to access Windows Update and Windows Server Update Services (WSUS). Scripts and programs can be written to examine which updates are currently available for a computer, and then you can install or uninstall updates.

to implement a search of the installed updates we need to use the IUpdateSearcher Interface using the Search method setting the search criteria.

check this sample

  //create the Com object instance
  updateSession:= CreateOleObject('Microsoft.Update.Session');
  updateSearcher    := updateSession.CreateUpdateSearcher;
  //set the search criteria, installed =1 means updates that are installed on the destination computer, Type='Software'  retrieve only applications updates
  updateSearchResult:= updateSearcher.Search(Format('IsInstalled = 1 and Type=%s',[QuotedStr('Software')]));

Now to find if a particular update is installed you can parse the Title property of the IUpdate Interface which contains the name of the update like this Security Update for Windows 7 for x64-based Systems (KB978542)

//use in this way ISHotFixID_Installed('KB982799')
function  ISHotFixID_Installed(const HotFixID : string): Boolean;
var
  updateSession      : OleVariant;
  updateSearcher     : OleVariant;
  updateEntry        : OleVariant;
  updateSearchResult : OleVariant;
  UpdateCollection   : OleVariant;
  oEnum              : IEnumvariant;
  iValue             : LongWord;
begin
 result:=False;
  updateSession:= CreateOleObject('Microsoft.Update.Session');
  updateSearcher    := updateSession.CreateUpdateSearcher;
  //this line improves the performance , the online porperty indicates whether the UpdateSearcher goes online to search for updates. so how we are looking for already installed updates we can set this value to false
  updateSearcher.online:=False;
  updateSearchResult:= updateSearcher.Search(Format('IsInstalled = 1 and Type=%s',[QuotedStr('Software')]));
  UpdateCollection  := updateSearchResult.Updates;
  oEnum         := IUnknown(UpdateCollection._NewEnum) as IEnumVariant;
  while oEnum.Next(1, updateEntry, iValue) = 0 do
  begin
    Result:=Pos(HotFixID,updateEntry.Title)>0;
    updateEntry:=Unassigned;
    if Result then break;
  end;
end;

check these another useful functions

Getting the installed updates list

procedure  GetListInstalledUpdates;
var
  updateSession        : OleVariant;
  updateSearcher       : OleVariant;
  updateSearchResult   : OleVariant;
  updateEntry          : OleVariant;
  UpdateCollection     : OleVariant;
  oEnum                : IEnumvariant;
  iValue               : LongWord;
begin
  updateSession:= CreateOleObject('Microsoft.Update.Session');
  updateSearcher := updateSession.CreateUpdateSearcher;
  Writeln('Searching');
  //IUpdateSearcher::Search Method http://msdn.microsoft.com/en-us/library/aa386526%28v=VS.85%29.aspx
  updateSearcher.online:=False;
  updateSearchResult:= updateSearcher.Search(Format('IsInstalled = 1 and Type=%s',[QuotedStr('Software')]));
  UpdateCollection  := updateSearchResult.Updates;
  oEnum         := IUnknown(UpdateCollection._NewEnum) as IEnumVariant;
  //IUpdate Interface http://msdn.microsoft.com/en-us/library/aa386099%28v=VS.85%29.aspx
  while oEnum.Next(1, updateEntry, iValue) = 0 do
  begin
    Writeln(updateEntry.Title);
    updateEntry:=Unassigned;
  end;
  Writeln('Done');
end;

Getting the not installed updates list (slow because need to check online)

procedure  GetListNotInstalledUpdates;
var
  updateSession        : OleVariant;
  updateSearcher       : OleVariant;
  updateSearchResult   : OleVariant;
  updateEntry          : OleVariant;
  UpdateCollection     : OleVariant;
  oEnum                : IEnumvariant;
  iValue               : LongWord;
begin
  updateSession:= CreateOleObject('Microsoft.Update.Session');
  updateSearcher := updateSession.CreateUpdateSearcher;
  Writeln('Searching');
  updateSearchResult:= updateSearcher.Search(Format('IsInstalled = 0 and Type=%s',[QuotedStr('Software')]));
  UpdateCollection  := updateSearchResult.Updates;
  oEnum         := IUnknown(UpdateCollection._NewEnum) as IEnumVariant;
  while oEnum.Next(1, updateEntry, iValue) = 0 do
  begin
    Writeln(updateEntry.Title);
    updateEntry:=Unassigned;
  end;
  Writeln('Done');
end;

Getting the hidden installed updates list

procedure  GetListInstalledHiddenUpdates;
var
  updateSession        : OleVariant;
  updateSearcher       : OleVariant;
  updateSearchResult   : OleVariant;
  updateEntry          : OleVariant;
  UpdateCollection     : OleVariant;
  oEnum                : IEnumvariant;
  iValue               : LongWord;
begin
  updateSession:= CreateOleObject('Microsoft.Update.Session');
  updateSearcher := updateSession.CreateUpdateSearcher;
  Writeln('Searching');
  updateSearcher.online:=False;
  updateSearchResult:= updateSearcher.Search(Format('IsHidden=1 and IsInstalled = 1 and Type=%s',[QuotedStr('Software')]));
  UpdateCollection  := updateSearchResult.Updates;
  oEnum         := IUnknown(UpdateCollection._NewEnum) as IEnumVariant;
  while oEnum.Next(1, updateEntry, iValue) = 0 do
  begin
    Writeln(updateEntry.Title);
    updateEntry:=Unassigned;
  end;
  Writeln('Done');
end;