The Road to Delphi

Delphi – Free Pascal – Oxygene


5 Comments

WMI Tasks using Delphi – Services

How do I determine which services are running and which ones are not?

Use the Win32_Service class to check the state of all of the services. The state property lets you know if a service is stopped or running.

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 Name, State FROM Win32_Service','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Name     %s',[String(FWbemObject.Name)]));// String
    Writeln(Format('State    %s',[String(FWbemObject.State)]));// String
    Writeln;
    FWbemObject:=Unassigned;
  end;
end;

How do I stop Power Users from starting certain services?

Use the Win32_Service class and the ChangeStartMode method to set the StartMode property to Disabled. Disabled services cannot be started, and, by default, Power Users cannot change the start mode of a service.

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_Service where StartMode = "Manual"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.Change( varEmpty, varEmpty, varEmpty, varEmpty, 'Disabled');
    FWbemObject:=Unassigned;
  end;
end;

How do I start and stop services?

Use the Win32_Service class and the StopService and StartService methods.

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_Service where Name  = "Alerter"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
    FWbemObject.StartService();
end;

How do I change service account passwords?

Use the Win32_Service class and the Change method.

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_Service where StartName = ".\netsvc"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
    FWbemObject.Change( varEmpty, varEmpty, varEmpty, varEmpty, varEmpty, varEmpty, varEmpty, 'password');
end;

How do I determine which services I can stop?

Use the Win32_Service class, and check the value of the AcceptStop property.

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_Service where AcceptStop = True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Name     %s',[String(FWbemObject.Name)]));// String
    FWbemObject:=Unassigned;
  end;
end;

How do I find the services that must be running before I can start the DHCP service?

Query for ASSOCIATORS OF the Win32_Service class named “DHCP” that are in the Win32_DependentService class and have “Dependent” in the Role property. Role means the role of the rasman service: in this case, it is antecedent to—must be started before—the dependent services.

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('Associators Of {Win32_Service.Name="dhcp"} Where AssocClass=Win32_DependentService Role=Dependent','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('%s  - %s',[String(FWbemObject.Name),String(FWbemObject.DisplayName)]));// String
    FWbemObject:=Unassigned;
  end;
end;

How do I find the services that require the WMI service (Winmgmt) service to be running before they can start?

Query for ASSOCIATORS OF the Win32_Service class named “winmgmt” that are in the Win32_DependentService class and have “Antecendent” in the Role property. Role means the role of the rasman service: in this case, it is antecedent to—must be started before—the dependent services.

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('Associators of {Win32_Service.Name="winmgmt"} Where AssocClass=Win32_DependentService Role=Antecedent','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('%s  - %s',[String(FWbemObject.Name),String(FWbemObject.DisplayName)]));// String
    FWbemObject:=Unassigned;
  end;
end;

This post is based in the MSDN Entry WMI Tasks: Services


Leave a comment

WMI Tasks using Delphi – Registry

How do I read registry key values using WMI?

Use the StdRegProv class, located in root\default  (for Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0)   and root\cimv2 namespace for newers versions of Windows . You cannot get any instances of this class because the System Registry Provider is a method and event provider only. However, you can get registry data through methods such as EnumKey or EnumValue. The Win32_Registry, located in root\cimv2 namespace, gets data about the registry as a whole, such as how large it is.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('GetDWORDValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Microsoft\Windows\CurrentVersion\BITS';
  FInParams.sValueName:='LogFileMinMemory';
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetDWORDValue', FInParams);
  Writeln(Format('sValue                %d',[Integer(FOutParams.uValue)]));
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));
end;

How do I create a new registry key?

Use the StdRegProv class, located in root\default namespace, and the CreateKey method.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('CreateKey').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'CreateKey', FInParams);
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));
end;

How do I create a new registry value under a key?

Use the StdRegProv class, located in the root\default namespace, and the CreateKey method. Then use one of the Set methods, depending on what registry datatype the value is, such as the SetDWORDValue. The Set methods create a value if it does not already exist. For more information, see Mapping a Registry Data Type to a WMI Data Type.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('SetExpandedStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FInParams.sValueName :='Example_Expanded_String_Value';
  FInParams.sValue     :='%PATHEXT%';
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetExpandedStringValue', FInParams);
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));
end;

How do I check security on a specific registry key?

Use the StdRegProv class, located in root\default namespace and the CheckAccess method. You can only check the access rights for the current user that is running the script or application. You cannot check the access rights for another specified user.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('CheckAccess').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SYSTEM\CurrentControlSet';
  FInParams.uRequired:=KEY_QUERY_VALUE;
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'CheckAccess', FInParams);
  Writeln(Format('Granted     %s',[booltoStr(Boolean(FOutParams.bGranted),True)]));
  Writeln(Format('ReturnValue %d',[Integer(FOutParams.ReturnValue)]));
end;

How do I read and write binary registry values?

Use the StdRegProv class, located in root\default namespace and the GetBinaryValue and SetBinaryValue methods. Registry values that appear in the regedt32 utility as a series of byte hexadecimal values are in the REG_BINARY data format. For more information, see Mapping a Registry Data Type to a WMI Data Type. The following code example creates a new key with a binary value. The binary value is supplied in the iValues byte array specified in Hex.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
  i               : Integer;

    function ArrayToVarArray(Arr : Array Of Word):OleVariant;
    var
     i : integer;
    begin
        Result   :=VarArrayCreate([0, High(Arr)], varVariant);
        for i:=Low(Arr) to High(Arr) do
         Result[i]:=Arr[i];
    end;

begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.Get('StdRegProv');

  //Write the binary value
  FInParams     := FWbemObjectSet.Methods_.Item('SetBinaryValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FInParams.sValueName :='Example Binary Value';
  FInParams.uValue     :=ArrayToVarArray([1,2,3,4,5,6,7,8]);
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetBinaryValue', FInParams);
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));

  //Read the binary value
  FInParams     := FWbemObjectSet.Methods_.Item('GetBinaryValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FInParams.sValueName :='Example Binary Value';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetBinaryValue', FInParams);
   for i := VarArrayLowBound(FOutParams.uValue, 1) to VarArrayHighBound(FOutParams.uValue, 1) do
    Write(Format('%d,',[Integer(FOutParams.uValue[i])]));

  Writeln;
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));

end;

How do I read and write registry values that contain multiple strings?

Use the StdRegProv class, located in root\default namespace and the GetMultiStringValue and SetMultiStringValue methods. Registry keys that appear in the regedt32 utility as a series of strings separated by spaces are in the REG_MULTI_SZ data format. For more information, see Mapping a Registry Data Type to a WMI Data Type. The following code example creates a new key and a new multistring value.

var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
  i               : Integer;

    function ArrayToVarArray(Arr : Array Of String):OleVariant;
    var
     i : integer;
    begin
        Result   :=VarArrayCreate([0, High(Arr)], varVariant);
        for i:=Low(Arr) to High(Arr) do
         Result[i]:=Arr[i];
    end;

begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\default', '', '');
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.Get('StdRegProv');

  //Write the Multi String Value
  FInParams     := FWbemObjectSet.Methods_.Item('SetMultiStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FInParams.sValueName :='Example Multistring Value';
  FInParams.sValue     :=ArrayToVarArray(['String 1','String 2','String 3','String 4','String 5','String 6','String 7','String 8']);
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetMultiStringValue', FInParams);
  Writeln(Format('ReturnValue %d',[Integer(FOutParams.ReturnValue)]));


  //Read the Multi String Value
  FInParams     := FWbemObjectSet.Methods_.Item('GetMultiStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\NewKey';
  FInParams.sValueName :='Example Multistring Value';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetMultiStringValue', FInParams);
   for i := VarArrayLowBound(FOutParams.sValue, 1) to VarArrayHighBound(FOutParams.sValue, 1) do
    Writeln(Format('%s',[String(FOutParams.sValue[i])]));

  Writeln;
  Writeln(Format('ReturnValue           %d',[Integer(FOutParams.ReturnValue)]));
end;

This article is based in the MSDN Entry WMI Tasks: Registry


Leave a comment

WMI Tasks using Delphi – Processes

How do I run an application in a hidden window?

Call the application from an app that uses the Win32_Process and Win32_ProcessStartup classes.

const
  wbemFlagForwardOnly = $00000020;
  HIDDEN_WINDOW       = 0;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObject   : OLEVariant;
  objProcess    : OLEVariant;
  objConfig     : OLEVariant;
  ProcessID     : Integer;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObject   := FWMIService.Get('Win32_ProcessStartup');
  objConfig     := FWbemObject.SpawnInstance_;
  objConfig.ShowWindow := HIDDEN_WINDOW;
  objProcess    := FWMIService.Get('Win32_Process');
  objProcess.Create('Notepad.exe', null, objConfig, ProcessID);
  Writeln(Format('Pid %d',[ProcessID]));
end;

How do I determine which scripts are running on the local computer?

Use the Win32_Process class and return all processes with the name Cscript.exe or Wscript.exe. To determine the individual scripts running in these processes, check the value of the CommandLine property.

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(Format('SELECT Name, CommandLine FROM Win32_Process Where Name="%s" or Name="%s"',['cscript.exe','wscript.exe']),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Name         %s',[String(FWbemObject.Name)]));
    Writeln(Format('Command Line %s',[String(FWbemObject.CommandLine)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I find out the account name under which a process is running?

Use the Win32_Process class and the GetOwner method.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  NameOfUser    : OleVariant;
  UserDomain    : OleVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_Process','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.GetOwner(NameOfUser, UserDomain);
    Writeln(Format('Process  %s is owned by %s\%s',[String(FWbemObject.Name),String(NameOfUser), String(UserDomain)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I change the priority of a running process?

Use the Win32_Process class and the SetPriority method.

const
  wbemFlagForwardOnly = $00000020;
  ABOVE_NORMAL = 32768;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  Value         : OleVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_Process Where Name="Notepad.exe"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Value:=ABOVE_NORMAL;
    FWbemObject.SetPriority(Value);
    FWbemObject:=Unassigned;
  end;
end;

How do I terminate a process?

Use the Win32_Process class and the Terminate method.

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_Process Where Name="Notepad.exe"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.Terminate();
    FWbemObject:=Unassigned;
  end;
end;

How do I determine how much processor time and memory each process is using?

Use the Win32_Process class and properties such as KernelModeTime, WorkingSetSize, PageFileUsage, and PageFaults.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  sngProcessTime: Double;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_Process','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Process          %s',[String(FWbemObject.Name)]));
    sngProcessTime := (Int64(FWbemObject.KernelModeTime) +  Int64(FWbemObject.UserModeTime)) / 10000000.0;
    Writeln(Format('Processor Time   %n',[sngProcessTime]));
    Writeln(Format('Process Id       %d',[Integer(FWbemObject.ProcessID)]));
    Writeln(Format('Working SetSize  %d',[Int64(FWbemObject.WorkingSetSize)]));
    Writeln(Format('Page File Usage  %d',[Int64(FWbemObject.PageFileUsage)]));
    Writeln(Format('Page Faults      %d',[Int64(FWbemObject.PageFaults)]));
    FWbemObject:=Unassigned;
  end;
end;

This article is based in the MSDN entry WMI Tasks: Processes


1 Comment

WMI Tasks using Delphi – Printers and Printing

How do I add a new printer connection to a remote computer?

Use the Win32_Printer class and the AddPrinterConnection method.

var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObject   : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObject   := FWMIService.Get('Win32_Printer');
  FWbemObject.AddPrinterConnection('\\PrintServer1\ArtDepartmentPrinter');
end;

How do I set the default printer?

Use the Win32_Printer class and the SetDefaultPrinter method.

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(Format('SELECT * FROM Win32_Printer Where Name = "%s"',['PrinterName']),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
    FWbemObject.SetDefaultPrinter();
end;

How do I cancel print jobs using WMI?

Use the Win32_Printer class, and the CancelAllJobs method.

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_Printer','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.CancelAllJobs();
    FWbemObject:=Unassigned;
  end;
end;

How do I determine the default printer for a computer?

Use the Win32_Printer class, and check whether the Default property is True.

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_Printer Where Default = True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('Name    %s',[String(FWbemObject.Name)]));// String
    FWbemObject:=Unassigned;
  end;
end;

This post is based in the MSDN entry WMI Tasks: Printers and Printing


12 Comments

Implementing a Delphi for..in loop on COM collections and Variant Arrays

Enumerating a collection of Variants

Many times when you are working with COM objects you need iterate over a collection, and usually the way to do this is using the _NewEnum function (which return a IUnknown interface) implemented by the COM class and then assign that value to a IEnumVariant variable. something like this

var
 Enum   : IEnumVariant;
 iValue : LongWord;
begin
 oEnum    := IUnknown(ACollection._NewEnum) as IEnumVariant;
  while oEnum.Next(1, AItem, iValue) = S_OK do
  begin
    //do something 
         
  end;

This not look very complicated, but when do you have to work with many nested COM collections, the code turns more difficult to follow. Check this sample :

begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  wmiDiskDrives := FWMIService.ExecQuery('SELECT Caption, DeviceID FROM Win32_DiskDrive','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(wmiDiskDrives._NewEnum) as IEnumVariant;//first enumerator
  while oEnum.Next(1, wmiDiskDrive, iValue) = 0 do
  begin
     DeviceID:=StringReplace(String(wmiDiskDrive.DeviceID),'\','\\',[rfReplaceAll]);
     wmiDiskPartitions := FWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="%s"} WHERE AssocClass = Win32_DiskDriveToDiskPartition',[String(DeviceID)]),'WQL',wbemFlagForwardOnly);
     oEnum2          := IUnknown(wmiDiskPartitions._NewEnum) as IEnumVariant;//second enumerator
     while oEnum2.Next(1, wmiDiskPartition, iValue) = 0 do
     begin
        wmiLogicalDisks := FWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="%s"} WHERE AssocClass = Win32_LogicalDiskToPartition',[String(wmiDiskPartition.DeviceID)]),'WQL',wbemFlagForwardOnly);
        oEnum3          := IUnknown(wmiLogicalDisks._NewEnum) as IEnumVariant;//third enumerator
        while oEnum3.Next(1, wmiLogicalDisk, iValue) = 0 do
        begin
          Writeln(Format('Drive letter associated with disk drive  %s %s Partition %s is %s',[String(wmiDiskDrive.Caption),String(wmiDiskDrive.DeviceID),String(wmiDiskPartition.DeviceID),String(wmiLogicalDisk.DeviceID)]));
          wmiLogicalDisk:=Unassigned;
        end;
       wmiDiskPartition:=Unassigned;
     end;
    wmiDiskDrive:=Unassigned;
    Writeln;
  end;
end;

Now look the same code using a for in loop in vbscript, which looks much more easy to follow and understeand

ComputerName = "."
Set wmiServices  = GetObject ("winmgmts:{impersonationLevel=Impersonate}!//" & ComputerName)
Set wmiDiskDrives =  wmiServices.ExecQuery("SELECT Caption, DeviceID FROM Win32_DiskDrive")

For Each wmiDiskDrive In wmiDiskDrives
    WScript.Echo "Disk drive Caption: " & wmiDiskDrive.Caption & VbNewLine & "DeviceID: " & " (" & wmiDiskDrive.DeviceID & ")"
    query = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='"  & wmiDiskDrive.DeviceID & "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition"    
    Set wmiDiskPartitions = wmiServices.ExecQuery(query)
    For Each wmiDiskPartition In wmiDiskPartition
        Set wmiLogicalDisks = wmiServices.ExecQuery ("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & wmiDiskPartition.DeviceID & "'} WHERE AssocClass = Win32_LogicalDiskToPartition") 
        For Each wmiLogicalDisk In wmiLogicalDisks
            WScript.Echo "Drive letter associated with disk drive = " & wmiDiskDrive.Caption & wmiDiskDrive.DeviceID & VbNewLine & " Partition = " & wmiDiskPartition.DeviceID & VbNewLine & " is " & wmiLogicalDisk.DeviceID
        Next      
    Next
Next

Now look back again the delphi code using for..in loop for iterate over the COM collection

begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  wmiDiskDrives := FWMIService.ExecQuery('SELECT Caption, DeviceID FROM Win32_DiskDrive','WQL',wbemFlagForwardOnly);
  for wmiDiskDrive in GetOleVariantEnum(wmiDiskDrives) do
  begin
     DeviceID:=StringReplace(String(wmiDiskDrive.DeviceID),'\','\\',[rfReplaceAll]);
     wmiDiskPartitions := FWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskDrive.DeviceID="%s"} WHERE AssocClass = Win32_DiskDriveToDiskPartition',[String(DeviceID)]),'WQL',wbemFlagForwardOnly);
     for wmiDiskPartition in GetOleVariantEnum(wmiDiskPartitions) do
     begin
        wmiLogicalDisks := FWMIService.ExecQuery(Format('ASSOCIATORS OF {Win32_DiskPartition.DeviceID="%s"} WHERE AssocClass = Win32_LogicalDiskToPartition',[String(wmiDiskPartition.DeviceID)]),'WQL',wbemFlagForwardOnly);
        for wmiLogicalDisk in GetOleVariantEnum(wmiLogicalDisks)  do
          Writeln(Format('Drive letter associated with disk drive  %s %s Partition %s is %s',[String(wmiDiskDrive.Caption),String(wmiDiskDrive.DeviceID),String(wmiDiskPartition.DeviceID),String(wmiLogicalDisk.DeviceID)]));
     end;
    Writeln;
  end;
end;

The delphi code now looks more cleaner and easy to follow. Now you are wondering how this was made? the answer is writting a class with an enumerator and a function which returns an interface with the implemented enumerator.

check how the class looks

type
  IOleVariantEnum  = interface
    function  GetCurrent: OLEVariant;
    function  MoveNext: Boolean;
    property  Current: OLEVariant read GetCurrent;
  end;

  IGetOleVariantEnum = interface
    function GetEnumerator: IOleVariantEnum;
  end;

  TOleVariantEnum = class(TInterfacedObject, IOleVariantEnum, IGetOleVariantEnum)
  private
    FCurrent : OLEVariant;
    FEnum    : IEnumVARIANT;
  public
    function GetEnumerator: IOleVariantEnum;
    constructor Create(Collection: OLEVariant);
    function  GetCurrent: OLEVariant;
    function  MoveNext: Boolean;
    property  Current: OLEVariant read GetCurrent;
  end;

As you see we have a base interface (IOleVariantEnum) with the methods to implement a enumerator, another interface (IGetOleVariantEnum) which return a enumerator and finally a class which descend from both interfaces and implements the logic of the enumerator.

The implementation of the last class is very simple and uses the IEnumVARIANT interface to iterate over the collection and return the current item.

constructor TOleVariantEnum.Create(Collection: OLEVariant);
begin
  inherited Create;
  FEnum := IUnknown(Collection._NewEnum) As IEnumVARIANT; //Set the COM enumerator for the variants collection
end;

function TOleVariantEnum.MoveNext: Boolean;
var
  iValue        : LongWord;
begin
  FCurrent:=Unassigned;//clear the previous value stored in FCurrent avoiding memory leaks
  Result:= FEnum.Next(1, FCurrent, iValue) = S_OK; //Get the next item in the collection into FCurrent
end;

function TOleVariantEnum.GetCurrent: OLEVariant;
begin
  Result:=FCurrent;
end;

function TOleVariantEnum.GetEnumerator: IOleVariantEnum;
begin
  Result:=Self;
end;

And now finally the function which makes the magic

function GetOleVariantEnum(Collection:OleVariant):IGetOleVariantEnum;
begin
 Result := TOleVariantEnum.Create(Collection);
end;

Now the good part of this is which you don’t need to worry of release the value returned by the GetOleVariantEnum function.

Finally you can rewrite your code using the TOleVariantEnum class

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: TOleVariantEnum;
  FWbemObject   : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= TOleVariantEnum.Create(FWMIService.ExecQuery('SELECT ProcessId FROM Win32_Process','WQL',wbemFlagForwardOnly));
  try
    for FWbemObject in FWbemObjectSet do
      Writeln(Format('Pid %d',[Integer(FWbemObject.ProcessId)]));
  finally
    FWbemObjectSet:=nil;
  end;
end;

or simply using the GetOleVariantEnum function.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT ProcessId FROM Win32_Process','WQL',wbemFlagForwardOnly);
  for FWbemObject in GetOleVariantEnum(FWbemObjectSet) do
    Writeln(Format('Pid %d',[Integer(FWbemObject.ProcessId)]));
end;

Enumerating a Variant Array

Tipically when you need iterate over a Variant Array you must get the bounds of the array using the VarArrayLowBound and VarArrayHighBound functions and from there you can access every item in the array only using the [] notation.

    for i := VarArrayLowBound(AvarArray, 1) to VarArrayHighBound(AvarArray, 1) do
     Item:=AvarArray[i];

Now using the same base interfaces you can implement a class to get a enumerator for this kind of arrays.

  TOleVariantArrayEnum = class(TInterfacedObject, IOleVariantEnum, IGetOleVariantEnum)
  private
    FCollection : OLEVariant;
    FIndex      : Integer;
    FLowBound   : Integer;
    FHighBound  : Integer;
  public
    function GetEnumerator: IOleVariantEnum;
    constructor Create(Collection: OLEVariant);
    function  GetCurrent: OLEVariant;
    function  MoveNext: Boolean;
    property  Current: OLEVariant read GetCurrent;
  end;

and the implementation

constructor TOleVariantArrayEnum.Create(Collection: OLEVariant);
begin
  inherited Create;
  FCollection:=Collection;
  FLowBound :=VarArrayLowBound(FCollection, 1);
  FHighBound:=VarArrayHighBound(FCollection, 1);
  FIndex:=FLowBound-1;
end;

function TOleVariantArrayEnum.GetCurrent: OLEVariant;
begin
  Result:=FCollection[FIndex];
end;

function TOleVariantArrayEnum.GetEnumerator: IOleVariantEnum;
begin
  Result:=Self;
end;

function TOleVariantArrayEnum.MoveNext: Boolean;
begin
  Result := FIndex < FHighBound;
  if Result then
    Inc(FIndex);
end;

and the helper function

function GetOleVariantArrEnum(Collection:OleVariant):IGetOleVariantEnum;
begin
 Result := TOleVariantArrayEnum.Create(Collection);
end;

And now you can iterate over the array using a for..in loop

  for Item in GetOleVariantArrEnum(AvarArray) do       

you can get the full source code of these classes and interfaces here.


3 Comments

WMI Tasks using Delphi – Operating Systems

How do I determine if a service pack has been installed on a computer?

Use the Win32_OperatingSystem class and check the value of the ServicePackMajorVersion and ServicePackMinorVersion properties.

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 ServicePackMajorVersion,ServicePackMinorVersion FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('ServicePack  %d.%d',[Integer(FWbemObject.ServicePackMajorVersion),Integer(FWbemObject.ServicePackMinorVersion)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine when the operating system was installed on a computer?

Use the Win32_OperatingSystem class and the InstallDate property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  FWbemDateObj  : OleVariant;
  Dt            : TDateTime;
begin;
  FWbemDateObj  := CreateOleObject('WbemScripting.SWbemDateTime');
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT InstallDate FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemDateObj.Value:=FWbemObject.InstallDate;
    Dt:=FWbemDateObj.GetVarDate;
    Writeln(Format('InstallDate %s',[FormatDateTime('dd mmm yyyy',dt)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine which version of the Windows operating system is installed on a computer?

Use the Win32_OperatingSystem class, and retrieve both the Name and Version properties.

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 Name, Version FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('%s %s',[String(FWbemObject.Name),String(FWbemObject.Version)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine which folder is the Windows folder (%Windir%) on a computer?

Use the Win32_OperatingSystem class, and check the value of the WindowsDirectory property.

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 WindowsDirectory  FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('Windows Path %s',[String(FWbemObject.WindowsDirectory)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine what hotfixes have been installed on a computer?

Use the Win32_QuickFixEngineering class. for more samples about this topic check this article search for installed windows updates using Delphi, WMI and WUA

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 Description,HotFixID  FROM Win32_QuickFixEngineering','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('HotFix %s',[String(FWbemObject.HotFixID)]));
    Writeln(Format('Description %s',[String(FWbemObject.Description)]));
    FWbemObject:=Unassigned;
  end;
end;

This entry is based in the MSDN article WMI Tasks: Operating Systems


Leave a comment

The WMI Delphi Code Creator now can create C++ Code compatible with Borland/Embarcadero compilers

After a lots of tests, I can officially announce which the WMI Delphi Code Creator now can create C++ Code compatible with the Borland/Embarcadero compilers. This new feature will be very helpful for developers using Borland/Embarcadero C++ compilers because exist very few documentation  about accessing the WMI from these compilers. The generated code can be compiled without any changes using the very old C++ Builder 5 until the modern C++ Builder XE2. The generated code uses the wbemcli.h library which is included in all the versions of  C++ builder. Another feature of this release is which the generated C++ code include all the security settings needed to establish local and remote WMI connections.

As always all your suggestions and comments are very welcome.

You can download the application from the  WMI Delphi Code Creator page.

C++ Code generated for the tool to access a WMI Class

#pragma hdrstop
#include <iostream>
using namespace std;
#include <wbemcli.h>
#include <comdef.h> 

//CREDENTIAL structure
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa374788%28v=vs.85%29.aspx
#define CRED_MAX_USERNAME_LENGTH            513
#define CRED_MAX_CREDENTIAL_BLOB_SIZE       512
#define CREDUI_MAX_USERNAME_LENGTH CRED_MAX_USERNAME_LENGTH
#define CREDUI_MAX_PASSWORD_LENGTH (CRED_MAX_CREDENTIAL_BLOB_SIZE / 2)

// The Win32_Process class represents a sequence of events on a Win32 system. Any sequence consisting of the interaction of one or more processors or interpreters, some executable code, and a set of inputs, is a descendent (or member) of this class.
// Example: A client application running on a Win32 system.

#pragma argsused
int main(int argc, char* argv[])
{
	wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH+1] = L"user";
	wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1]  = L"password";
	BSTR strNetworkResource;
	//To use a WMI remote connection set localconn to false and configure the values of the pszName, pszPwd and the name of the remote machine in strNetworkResource
	bool localconn = true;	
	strNetworkResource = localconn ?  L"\\\\.\\root\\CIMV2" : L"\\\\remote--machine\\root\\CIMV2";

	COAUTHIDENTITY *userAcct =  NULL ;
	COAUTHIDENTITY authIdent;

	// Initialize COM. ------------------------------------------

	HRESULT hres;
	hres =  CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres))
	{
        cout << "Failed to initialize COM library. Error code = 0x"	<< hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        cout << "press enter to exit" << endl;
        cin.get();		
        return 1;                  // Program has failed.
	}

	// Set general COM security levels --------------------------

	if (localconn)
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
	else
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IDENTIFY,    // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
			
	if (FAILED(hres))
	{
        cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();
		cout << "press enter to exit" << endl;
	    cin.get();		
        return 1;                    // Program has failed.
	}

	// Obtain the initial locator to WMI -------------------------

	IWbemLocator *pLoc = NULL;
	hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);

	if (FAILED(hres))
	{
        cout << "Failed to create IWbemLocator object."	<< " Err code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();	    
        cout << "press enter to exit" << endl;
		cin.get();		
        return 1;                 // Program has failed.
	}

	// Connect to WMI through the IWbemLocator::ConnectServer method

	IWbemServices *pSvc = NULL;

	if (localconn)	
		hres = pLoc->ConnectServer(
			 strNetworkResource,      // Object path of WMI namespace
			 NULL,                    // User name. NULL = current user
			 NULL,                    // User password. NULL = current
			 0,                       // Locale. NULL indicates current
			 NULL,                    // Security flags.
			 0,                       // Authority (e.g. Kerberos)
			 0,                       // Context object
			 &pSvc                    // pointer to IWbemServices proxy
			 );
	else
		hres = pLoc->ConnectServer(
			strNetworkResource,  // Object path of WMI namespace
			pszName,             // User name
			pszPwd,              // User password
			NULL,                // Locale
			NULL,                // Security flags
			NULL,				 // Authority
			NULL,                // Context object
			&pSvc                // IWbemServices proxy
			);

	if (FAILED(hres))
	{
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;	
        cout << _com_error(hres).ErrorMessage() << endl;
        pLoc->Release();
        CoUninitialize();
	    cout << "press enter to exit" << endl;
	    cin.get();			
        return 1;                // Program has failed.
	}

	cout << "Connected to root\\CIMV2 WMI namespace" << endl;

    // Set security levels on the proxy -------------------------
	if (localconn)
		hres = CoSetProxyBlanket(
		   pSvc,                        // Indicates the proxy to set
		   RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
		   NULL,                        // Server principal name
		   RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
		   NULL,                        // client identity
		   EOAC_NONE                    // proxy capabilities
		);
	else
	{
		// Create COAUTHIDENTITY that can be used for setting security on proxy
		memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
		authIdent.PasswordLength = wcslen (pszPwd);
		authIdent.Password = (USHORT*)pszPwd;
		authIdent.User = (USHORT*)pszName;
		authIdent.UserLength = wcslen(pszName);
		authIdent.Domain = 0;
		authIdent.DomainLength = 0;
		authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
		userAcct = &authIdent;

		hres = CoSetProxyBlanket(
		   pSvc,                           // Indicates the proxy to set
		   RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
		   COLE_DEFAULT_PRINCIPAL,         // Server principal name
		   RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
		   userAcct,                       // client identity
		   EOAC_NONE                       // proxy capabilities
		);
	}

	if (FAILED(hres))
	{
        cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();		
		return 1;               // Program has failed.
	}

	// Use the IWbemServices pointer to make requests of WMI ----

	IEnumWbemClassObject* pEnumerator = NULL;
	hres = pSvc->ExecQuery(	L"WQL",	L"SELECT * FROM Win32_Process",
	WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

	if (FAILED(hres))
	{
		cout << "ExecQuery failed" << " Error code = 0x"	<< hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();		
		return 1;               // Program has failed.
	}
    
	// Secure the enumerator proxy
	if (!localconn)
	{
		
		hres = CoSetProxyBlanket(
			pEnumerator,                    // Indicates the proxy to set
			RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
			RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
			COLE_DEFAULT_PRINCIPAL,         // Server principal name
			RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx
			RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
			userAcct,                       // client identity
			EOAC_NONE                       // proxy capabilities
			);

		if (FAILED(hres))
		{
			cout << "Could not set proxy blanket on enumerator. Error code = 0x" << hex << hres << endl;
			cout << _com_error(hres).ErrorMessage() << endl;
			pEnumerator->Release();
			pSvc->Release();
			pLoc->Release();
			CoUninitialize();
			cout << "press enter to exit" << endl;
			cin.get();				
			return 1;               // Program has failed.
		}
	}

	// Get the data from the WQL sentence
	IWbemClassObject *pclsObj = NULL;
	ULONG uReturn = 0;

	while (pEnumerator)
	{
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

		if(0 == uReturn || FAILED(hr))
		  break;

		VARIANT vtProp;

                hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);// String
                if (!FAILED(hr))
                {
                  if ((vtProp.vt==VT_NULL) || (vtProp.vt==VT_EMPTY))
                    wcout << "Name : " << ((vtProp.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
                  else
                  if ((vtProp.vt & VT_ARRAY))
                    wcout << "Name : " << "Array types not supported (yet)" << endl;
                  else
                    wcout << "Name : " << vtProp.bstrVal << endl;
                }
                VariantClear(&vtProp);

                hr = pclsObj->Get(L"ProcessId", 0, &vtProp, 0, 0);// Uint32
                if (!FAILED(hr))
                {
                  if ((vtProp.vt==VT_NULL) || (vtProp.vt==VT_EMPTY))
                    wcout << "ProcessId : " << ((vtProp.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
                  else
                  if ((vtProp.vt & VT_ARRAY))
                    wcout << "ProcessId : " << "Array types not supported (yet)" << endl;
                  else
                    wcout << "ProcessId : " << vtProp.uintVal << endl;
                }
                VariantClear(&vtProp);

		
		pclsObj->Release();
		pclsObj=NULL;
	}

	// Cleanup

	pSvc->Release();
	pLoc->Release();
	pEnumerator->Release();
	if (pclsObj!=NULL)
	 pclsObj->Release();

	CoUninitialize();
	cout << "press enter to exit" << endl;
	cin.get();
	return 0;   // Program successfully completed.
}

C++ Code generated for the tool to execute a WMI Method

#pragma hdrstop
#include <iostream>
using namespace std;
#include <wbemcli.h>
#include <comdef.h>

//CREDENTIAL structure
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa374788%28v=vs.85%29.aspx
#define CRED_MAX_USERNAME_LENGTH            513
#define CRED_MAX_CREDENTIAL_BLOB_SIZE       512
#define CREDUI_MAX_USERNAME_LENGTH CRED_MAX_USERNAME_LENGTH
#define CREDUI_MAX_PASSWORD_LENGTH (CRED_MAX_CREDENTIAL_BLOB_SIZE / 2)

// The Create method creates a new process. 
// The method returns an integer value that can be interpretted as follows: 
// 0 - Successful completion.
// 2 - The user does not have access to the requested information.
// 3 - The user does not have sufficient privilge.
// 8 - Unknown failure.
// 9 - The path specified does not exist.
// 21 - The specified parameter is invalid.
// Other - For integer values other than those listed above, refer to Win32 error code documentation.

#pragma argsused
int main(int argc, char* argv[])
{
	wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH+1] = L"user";
	wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1]  = L"password";
	BSTR strNetworkResource;
	//To use a WMI remote connection set localconn to false and configure the values of the pszName, pszPwd and the name of the remote machine in strNetworkResource
	bool localconn = true;	
	strNetworkResource = localconn ?  L"\\\\.\\root\\CIMV2" : L"\\\\remote--machine\\root\\CIMV2";

	COAUTHIDENTITY *userAcct =  NULL ;
	COAUTHIDENTITY authIdent;
	
	HRESULT hres;

	// Initialize COM. ------------------------------------------

	hres =  CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres))
	{
		cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		cout << "press enter to exit" << endl;
		cin.get();			
		return 1;                  // Program has failed.
	}

	// Set general COM security levels --------------------------

	if (localconn)
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
	else
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IDENTIFY,    // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
			
                      
	if (FAILED(hres))
	{
		cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();			
		return 1;                      // Program has failed.
	}
    
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
 
	if (FAILED(hres))
	{
		cout << "Failed to create IWbemLocator object. " << "Err code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();				
		return 1;                 // Program has failed.
	}

    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;
	
    // Connect to the root\\CIMV2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
	if (localconn)	
		hres = pLoc->ConnectServer(
			 strNetworkResource,      // Object path of WMI namespace
			 NULL,                    // User name. NULL = current user
			 NULL,                    // User password. NULL = current
			 0,                       // Locale. NULL indicates current
			 NULL,                    // Security flags.
			 0,                       // Authority (e.g. Kerberos)
			 0,                       // Context object
			 &pSvc                    // pointer to IWbemServices proxy
			 );
	else
		hres = pLoc->ConnectServer(
			strNetworkResource,  // Object path of WMI namespace
			pszName,             // User name
			pszPwd,              // User password
			NULL,                // Locale
			NULL,                // Security flags
			NULL,				 // Authority
			NULL,                // Context object
			&pSvc                // IWbemServices proxy
			);
			
			
	if (FAILED(hres))
	{
		cout << "Could not connect. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pLoc->Release();
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();					
		return 1;                // Program has failed.
	}

	cout << "Connected to root\\CIMV2 WMI namespace" << endl;

    // Set security levels on the proxy -------------------------
	if (localconn)
		hres = CoSetProxyBlanket(
		   pSvc,                        // Indicates the proxy to set
		   RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
		   NULL,                        // Server principal name
		   RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
		   NULL,                        // client identity
		   EOAC_NONE                    // proxy capabilities
		);
	else
	{
		// Create COAUTHIDENTITY that can be used for setting security on proxy
		memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
		authIdent.PasswordLength = wcslen (pszPwd);
		authIdent.Password = (USHORT*)pszPwd;
		authIdent.User = (USHORT*)pszName;
		authIdent.UserLength = wcslen(pszName);
		authIdent.Domain = 0;
		authIdent.DomainLength = 0;
		authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
		userAcct = &authIdent;

		hres = CoSetProxyBlanket(
		   pSvc,                           // Indicates the proxy to set
		   RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
		   COLE_DEFAULT_PRINCIPAL,         // Server principal name
		   RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
		   userAcct,                       // client identity
		   EOAC_NONE                       // proxy capabilities
		);
	}
	
	
	if (FAILED(hres))
	{
		cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pSvc->Release();
		pLoc->Release();     
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();					
		return 1;               // Program has failed.
	}

    // Use the IWbemServices pointer to make requests of WMI ----

    BSTR MethodName = SysAllocString(L"Create");
    BSTR ClassName = SysAllocString(L"Win32_Process");

    IWbemClassObject* pClass = NULL;
    hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);

    IWbemClassObject* pInParamsDefinition = NULL;
    hres = pClass->GetMethod(MethodName, 0, &pInParamsDefinition, NULL);

    IWbemClassObject* pClassInstance = NULL;
    hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);

    VARIANT varCommand;
    varCommand.vt = VT_BSTR;
    varCommand.bstrVal = L"cmd.exe";
    hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0);
    VariantClear(&varCommand);
	

	// Execute Method
	IWbemClassObject* pOutParams = NULL;
	hres = pSvc->ExecMethod(ClassName, MethodName, 0,
	NULL, pClassInstance, &pOutParams, NULL);

	if (FAILED(hres))
	{
		cout << "Could not execute method. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		SysFreeString(ClassName);
		SysFreeString(MethodName);
		if (pClass)			
		pClass->Release();
		if (pInParamsDefinition)			
		pInParamsDefinition->Release();
		if (pOutParams)			
		pOutParams->Release();
		if (pSvc)			
		pSvc->Release();
		if (pLoc)			
		pLoc->Release();     
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();			
		return 1;               // Program has failed.
	}


	
    VARIANT varReturnValue;
    hres = pOutParams->Get(L"ProcessId", 0, &varReturnValue, NULL, 0);
    if (!FAILED(hres))
    {
      if ((varReturnValue.vt==VT_NULL) || (varReturnValue.vt==VT_EMPTY))
        wcout << "ProcessId : " << ((varReturnValue.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
      else
      if ((varReturnValue.vt & VT_ARRAY))
        wcout << "ProcessId : " << "Array types not supported (yet)" << endl;
      else
        wcout << "ProcessId : " << varReturnValue.uintVal << endl;
    }
    VariantClear(&varReturnValue);

    hres = pOutParams->Get(L"ReturnValue", 0, &varReturnValue, NULL, 0);
    if (!FAILED(hres))
    {
      if ((varReturnValue.vt==VT_NULL) || (varReturnValue.vt==VT_EMPTY))
        wcout << "ReturnValue : " << ((varReturnValue.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
      else
      if ((varReturnValue.vt & VT_ARRAY))
        wcout << "ReturnValue : " << "Array types not supported (yet)" << endl;
      else
        wcout << "ReturnValue : " << varReturnValue.uintVal << endl;
    }
    VariantClear(&varReturnValue);

	

    // Clean up    
    SysFreeString(ClassName);
    SysFreeString(MethodName);
    pClass->Release();
    pInParamsDefinition->Release();
    pOutParams->Release();
    pLoc->Release();
    pSvc->Release();
    CoUninitialize();
    cout << "press enter to exit" << endl;
    cin.get();	
    return 0;
}


15 Comments

WMI Tasks using Delphi – Networking

How do I disable a network connection using WMI?

If you are using DHCP, use the Win32_NetworkAdapterConfiguration and the ReleaseDHCPLease method to release the IP address. If you are not using DHCP, you cannot use WMI to disable a network connection. To re-enable the network connection, use objNetCard.RenewDHCPLease. You can also release or renew all of the DHCP leases using the ReleaseDHCPLeaseAll and RenewDHCPLeaseAll methods.

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_NetworkAdapterConfiguration Where IPEnabled = True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.ReleaseDHCPLease();
    FWbemObject:=Unassigned;
  end;
end;

How do I disable or enable a NIC?

Use the Win32_NetworkAdapter class and the Disable or Enable methods.

//Disable a NIC
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_NetworkAdapter Where NetEnabled=True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.Disable();
    FWbemObject:=Unassigned;
  end;
end;
//Enable a NIC
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_NetworkAdapter Where NetEnabled=False','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.Enable();
    FWbemObject:=Unassigned;
  end;
end;

How do I determine which IP address has been assigned to a given network connection?

Use the Win32_NetworkAdapter class and the NetConnectionID property to determine the MAC address of the network connection. Then, use the Win32_NetworkAdapterConfiguration class to find the IP address associated with the MAC address.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  strMACAddress : string;
  i             : Integer;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');

  FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT MACAddress FROM Win32_NetworkAdapter Where NetConnectionID="%s"',['Local Area Connection 2']),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    strMACAddress:=String(FWbemObject.MACAddress);
    FWbemObject:=Unassigned;
  end;

  FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT IPAddress FROM Win32_NetworkAdapterConfiguration Where MACAddress="%s"',[strMACAddress]),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    if not VarIsClear(FWbemObject.IPAddress) then
    for i := VarArrayLowBound(FWbemObject.IPAddress, 1) to VarArrayHighBound(FWbemObject.IPAddress, 1) do
     Writeln(Format('IP Address %s',[String(FWbemObject.IPAddress[i])]));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine the MAC address of a network adapter?

Use the Win32_NetworkAdapterConfiguration class and check the value of the MACAddress property.

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 Description,MACAddress FROM Win32_NetworkAdapterConfiguration','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Description %s',[String(FWbemObject.Description)]));
    if not VarIsNull(FWbemObject.MACAddress) then
      Writeln(Format('  MAC Address %s',[String(FWbemObject.MACAddress)]))
    else
      Writeln(Format('  MAC Address %s',['Empty']));
    FWbemObject:=Unassigned;
  end;
end;

How do I determine the IP address(es) of a computer?

Use the Win32_NetworkAdapterConfiguration class and check the value of the IPAddress property.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  i             : Integer;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT IPAddress FROM Win32_NetworkAdapterConfiguration','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    if not VarIsClear(FWbemObject.IPAddress) and not VarIsNull(FWbemObject.IPAddress) then
    for i := VarArrayLowBound(FWbemObject.IPAddress, 1) to VarArrayHighBound(FWbemObject.IPAddress, 1) do
     Writeln(Format('IP Address %s',[String(FWbemObject.IPAddress[i])]));
    FWbemObject:=Unassigned;
  end;
end;

How do I configure a computer to start getting its IP address through DHCP?

Use the Win32_NetworkAdapterConfiguration class and the EnableDHCP method.

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_NetworkAdapterConfiguration Where IPEnabled=True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    FWbemObject.EnableDHCP();
    FWbemObject:=Unassigned;
  end;
end;

How do I assign a computer a static IP address?

Use the Win32_NetworkAdapterConfiguration class and the EnableStatic method.

function ArrayToVarArray(Arr : Array Of string):OleVariant; overload;
var
 i : integer;
begin
    Result   :=VarArrayCreate([0, High(Arr)], varVariant);
    for i:=Low(Arr) to High(Arr) do
     Result[i]:=Arr[i];
end;

function ArrayToVarArray(Arr : Array Of Word):OleVariant;overload;
var
 i : integer;
begin
    Result   :=VarArrayCreate([0, High(Arr)], varVariant);
    for i:=Low(Arr) to High(Arr) do
     Result[i]:=Arr[i];
end;

procedure  SetStaticIP;
const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  vIPAddress         : OleVariant;
  vSubnetMask        : OleVariant;
  vDefaultIPGateway  : OleVariant;
  vGatewayCostMetric : OleVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_NetworkAdapterConfiguration Where IPEnabled=True','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    vIPAddress   := ArrayToVarArray(['192.168.1.141']);
    vSubnetMask  := ArrayToVarArray(['255.255.255.0']);
    if FWbemObject.EnableStatic(vIPAddress, vSubnetMask) = 0 then
    begin
      vDefaultIPGateway  := ArrayToVarArray(['192.168.1.100']);
      vGatewayCostMetric := ArrayToVarArray([1]);
      FWbemObject.SetGateways(vDefaultIPGateway,vGatewayCostMetric);
    end;

    VarClear(vIPAddress);
    VarClear(vSubnetMask);
    VarClear(vDefaultIPGateway);
    VarClear(vGatewayCostMetric);
    FWbemObject:=Unassigned;
  end;
end;

How do I ping a computer without using Ping.exe?

Use the Win32_PingStatus class. Starting with Windows Vista, Win32_PingStatus can return data for computers that have both IPv4 addresses and IPv6 addresses.

For a delphi sample code check this article Making a PING with Delphi and the WMI.

This post is based in the MSDN entry WMI Tasks: Networking


4 Comments

WMI Tasks using Delphi – Files and Folders

How do I rename a local or remote file?

Use the CIM_DataFile class. Ensure that you pass the entire path name when calling the Rename method, for example, “C:\Foo\Test.txt” instead of “Text.txt”.

const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  FileName      : string;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FileName      := 'C:\\Foo\\Test.txt';//look how the name of the file to rename is escaped
  FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT * FROM CIM_DataFile Where Name="%s"',[FileName]),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemObject.Rename('C:\Foo\Test_new.txt');
    FWbemObject:=Unassigned;
  end;
end;

How do I determine whether users have .MP3 files stored on their computer?

Use the CIM_DataFile class and select files using the following WQL WHERE clause: Where Extension = “MP3”.

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(Format('SELECT Name,Extension,Path FROM CIM_DataFile Where Extension="%s"',['mp3']),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('File Name %s.%s',[String(FWbemObject.Name),String(FWbemObject.Extension)]));
    Writeln(Format('Path      %s',[String(FWbemObject.Path)]));
    FWbemObject:=Unassigned;
  end;
end;

How do I create shared folders on a computer?

Use the Win32_Share class and the Create method.

const
  FILE_SHARE          = 0;
  MAXIMUM_CONNECTIONS = 25;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObject   : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObject   := FWMIService.Get('Win32_Share');
  FWbemObject.Create('C:\Finance', 'FinanceShare', FILE_SHARE, MAXIMUM_CONNECTIONS, 'Public share for the Finance group.');
end;

For more samples of using the WMI and Delphi to handle files and folders in local and remote machines check this article Manipulating local/remote files and folders using Delphi and WMI

This post is based in the MSDN entry WMI Tasks: Files and Folders


11 Comments

WMI Tasks using Delphi – Event Logs

How do I retrieve information about the Security event log?

Use the Win32_NTEventlogFile class.

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 MaxFileSize ,NumberOfRecords FROM Win32_NTEventlogFile Where LogFileName="Security"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Max File Size        %d',[Integer(FWbemObject.MaxFileSize)]));
    Writeln(Format('Number Of Records    %d',[Integer(FWbemObject.NumberOfRecords)]));
    Writeln;
    FWbemObject:=Unassigned;
  end;
end;

How do I back up an event log?

Use the Win32_NTEventlogFile class and the BackupEventLog method

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_NTEventlogFile Where LogFileName="Application"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemObject.BackupEventLog('c:\logs\backup.evt');
    FWbemObject:=Unassigned;
  end;
end;

How do I back up an event log more than once?

Ensure that the backup file has a unique name before using the Win32_NTEventlogFile and the BackupEventLog method. The operating system does not allow you to overwrite an existing backup file; you must either move the backup file or rename it before you can run the script again.

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_NTEventlogFile Where LogFileName="Application"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemObject.BackupEventLog(Format('c:\logs\backup_%s.evt',[FormatDateTime('hhnnsszzz',Now)]));
    FWbemObject.ClearEventLog();
    FWbemObject:=Unassigned;
  end;
end;

How do I determine the number of records in an event log?

Use the Win32_NTEventlogFile class and check the value of the NumberOfRecords property.

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 NumberOfRecords FROM Win32_NTEventlogFile Where LogFileName="Security"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('Number Of Records    %d',[Integer(FWbemObject.NumberOfRecords)]));
    Writeln;
    FWbemObject:=Unassigned;
  end;
end;

How do I clear my event logs?

Use the Win32_NTEventlogFile class and the ClearEventLog method.

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_NTEventlogFile Where LogFileName="Application"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    FWbemObject.ClearEventLog();
    FWbemObject:=Unassigned;
  end;
end;

How do I read events from the event logs?

Use the Win32_NTLogEvent class.

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_NTLogEvent  Where Logfile="System"','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Category          %s',[String(FWbemObject.Category)]));
    Writeln(Format('Computer Name     %s',[String(FWbemObject.ComputerName)]));
    Writeln(Format('EventCode         %d',[Integer(FWbemObject.EventCode)]));
    Writeln(Format('Message           %s',[String(FWbemObject.Message)]));
    Writeln(Format('RecordNumber      %d',[Integer(FWbemObject.RecordNumber)]));
    FWbemObject:=Unassigned;
  end;
end;

This post is based in the MSDN entry WMI Tasks: Event Logs