2

Currently I'm using this function. It works fine but each query takes about 1 second. So in my case I waste 3 seconds in my application. At the moment I'm thinking about using 3 threads to get this all information in one second.

function GetWMIstring (wmiHost, wmiClass, wmiProperty : string):string;
var  // These are all needed for the WMI querying process
  Locator:  ISWbemLocator;
  Services: ISWbemServices;
  SObject:  ISWbemObject;
  ObjSet:   ISWbemObjectSet;
  SProp:    ISWbemProperty;
  Enum:     IEnumVariant;
  Value:    Cardinal;
  TempObj:  OleVariant;
  SN: string;
begin
  Result := '';
  try
  Locator := CoSWbemLocator.Create;  // Create the Location object
  // Connect to the WMI service, with the root\cimv2 namespace
  Services := Locator.ConnectServer(wmiHost, 'root\cimv2', '', '', '','', 0, nil);
  ObjSet := Services.ExecQuery('SELECT * FROM '+wmiClass, 'WQL',
    wbemFlagReturnImmediately and wbemFlagForwardOnly , nil);
  Enum := (ObjSet._NewEnum) as IEnumVariant;
  while Enum.Next(1, TempObj, Value) = S_OK do
  begin
    try SObject := IUnknown(TempObj) as ISWBemObject; except SObject := nil; end;
    TempObj := Unassigned;  // Always need to free interface in TempObj
    if SObject <> nil then
    begin
      SProp := SObject.Properties_.Item(wmiProperty, 0);
      SN := SProp.Get_Value;
      if not VarIsNull(SN) then
      begin
        Result :=  SN;
        Break;
      end;
    end;
  end;
  except // Trap any exceptions (Not having WMI installed will cause one!)
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin

  CoInitializeEx(nil,COINIT_MULTITHREADED) ; //required !!! Otherwise "EOleSysError: CoInitialize has not been called" occurs   (Microsoft recommends CoInitializeEx instead of CoInitialize)

  CPU_PROCESSOR_COUNT := StrToInt( getWMIstring('','Win32_ComputerSystem','NumberOfProcessors') );   // number of cpu on mainboard
  CPU_PHYSICAL_CORES := StrToInt( getWMIstring('','Win32_Processor','NumberOfCores') );      // number of phisical cores
  CPU_LOGICAL_CORES := StrToInt( getWMIstring('','Win32_Processor','NumberOfLogicalProcessors') );     //number of logical cores

  CoUninitialize; //required !!!

end;
J...
  • 30,968
  • 6
  • 66
  • 143
Atak_Snajpera
  • 619
  • 9
  • 24
  • 3
    Adding more threads is the wrong solution. Use `GetLogicalProcessorInformation` or `GetLogicalProcessorInformationEx` instead. Also, your call to `CoInitializeEx` is very dubious. A VCL app by default initializes COM in the main thread. Did you do something to break that. Further, you can't just initialize COM, and finalize it at arbitrary points in a thread's lifetime. That is bound to cause conflicts. Anyway, once you remove the WMI, you won't have to worry about that but it is concerning that you seem to have broken COM initialisation somewhere else. – David Heffernan Sep 07 '17 at 10:12
  • 1
    I would use [this](https://theroadtodelphi.com/2013/03/27/getting-processor-info-using-object-pascal-delphi-fpc-and-the-tsmbios/) – whosrdaddy Sep 07 '17 at 10:54
  • @DavidHeffernan From the context I would guess that OP is executing `Button1Click` from a thread (hence the COM exception). Definitely not the right way to handle it, of course. – J... Sep 07 '17 at 11:17
  • @J... Pretty hard to imagine that is the case. Button clicks are fired by the framework, in the main thread – David Heffernan Sep 07 '17 at 11:18
  • @DavidHeffernan I suspect because you have a poor imagination when it comes to writing bad code. I can't count how many times I see things like `Button1Click(nil);` in a method somewhere. OP is experimenting with threads... I can *easily* see that they did something like that and got stung with `EOleSysError`. – J... Sep 07 '17 at 11:20
  • 2
    @DavidHeffernan: where do I propose WMI? The uSMBIOS Unit from RRUZ uses WinApi (but also WMI if you define a flag)? – whosrdaddy Sep 07 '17 at 11:23
  • @Atak_Snapeira, you are not using the WMI in a proper way, you are connecting multiple times to the namespace and also you are retrieving all the properties of the WMI class, check this question for some tips about improving the performance https://stackoverflow.com/questions/10199531/how-can-i-improve-the-wmi-performance-using-delphi – RRUZ Sep 07 '17 at 17:55

2 Answers2

2

If you're interested in the number of logical processors - say for thread scheduling purposes, there is much easier and earlier GetSystemInfo function:

function GetNumberOfProcessors: Integer;
var
   si: TSystemInfo; //Windows.pas
begin
   GetSystemInfo({var}si);
   Result := si.dwNumberOfProcessors;
end;

For nearly all purposes:

  • it doesn't matter if they are individual physical CPUs
  • or if they're cores
  • or multiple physical CPUs each with multiple cores
  • or if they're hyperthreading virtual cores

You want what GetSystemInfo provides.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
1

Here is an example using GetLogicalProcessorInformation :

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

type
  TLogicalProcessorInformation = record
    LogicalProcessorCount : integer;
    NumaNodeCount : integer;
    ProcessorCoreCount : integer;
    ProcessorL1CacheCount : integer;
    ProcessorL2CacheCount : integer;
    ProcessorL3CacheCount : integer;
    ProcessorPackageCount : integer;
  end;

function CountSetBits(bitMask : NativeUInt) : integer;
var
  lShift, i : integer;
  bitTest : NativeUInt;
begin
  lShift := SizeOf(NativeUInt)*8 - 1;
  result := 0;
  bitTest := 1 shl lShift;    
  for i := 0 to lShift do begin
    if (bitMask and bitTest) <> 0 then Inc(result);
    bitTest := bitTest shr 1;
  end;
end;

function GetLogicalProcessorInfo : TLogicalProcessorInformation;
var
  i: Integer;
  ReturnLength: DWORD;
  Buffer: array of TSystemLogicalProcessorInformation;
begin
  result.LogicalProcessorCount := 0;
  result.NumaNodeCount := 0;
  result.ProcessorCoreCount := 0;
  result.ProcessorL1CacheCount := 0;
  result.ProcessorL2CacheCount := 0;
  result.ProcessorL3CacheCount := 0;
  result.ProcessorPackageCount := 0;
  SetLength(Buffer, 256);
  if not GetLogicalProcessorInformation(@Buffer[0], ReturnLength) then
  begin
    if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
      SetLength(Buffer,
        ReturnLength div SizeOf(TSystemLogicalProcessorInformation) + 1);
      if not GetLogicalProcessorInformation(@Buffer[0], ReturnLength) then
        RaiseLastOSError;
    end else
      RaiseLastOSError;
  end;
  SetLength(Buffer, ReturnLength div SizeOf(TSystemLogicalProcessorInformation));

  for i := 0 to High(Buffer) do begin
    case Buffer[i].Relationship of
        RelationNumaNode: Inc(result.NumaNodeCount);
        RelationProcessorCore:
          begin
            Inc(result.ProcessorCoreCount);
            result.LogicalProcessorCount := result.LogicalProcessorCount + CountSetBits(Buffer[i].ProcessorMask);
          end;
        RelationCache:
          begin
            if (Buffer[i].Cache.Level = 1) then Inc(result.ProcessorL1CacheCount)
            else if (Buffer[i].Cache.Level = 2) then Inc(result.ProcessorL2CacheCount)
            else if (Buffer[i].Cache.Level = 3) then Inc(result.ProcessorL3CacheCount);
          end;
        RelationProcessorPackage: Inc(result.ProcessorPackageCount);
        else
          raise Exception.Create('Error: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.');
    end;
  end;
end;

var
  LProcInfo : TLogicalProcessorInformation;
begin
  LProcInfo := GetLogicalProcessorInfo;
  WriteLn('Logical processor count = ', LProcInfo.LogicalProcessorCount);
  WriteLn('NUMA Node count = ', LProcInfo.NumaNodeCount);
  WriteLn('Processor Core count = ', LProcInfo.ProcessorCoreCount);
  WriteLn('L1 Cache count = ', LProcInfo.ProcessorL1CacheCount);
  WriteLn('L2 Cache count = ', LProcInfo.ProcessorL2CacheCount);
  WriteLn('L3 Cache count = ', LProcInfo.ProcessorL3CacheCount);
  WriteLn('Package count = ', LProcInfo.ProcessorPackageCount);
  ReadLn;
end.

For XE2 and higher these WinAPI definitions are included in the RTL. The definitions can otherwise be included manually. An example implementation can be found here.

For a desktop i7 workstation, for example, this outputs :

Logical processor count = 8
NUMA Node count = 1
Processor Core count = 4
L1 Cache count = 8
L2 Cache count = 4
L3 Cache count = 1
Package count = 1

The logical core count includes hyperthread virtual cores, the processor core count is the physical number of cores, the package count returns the number of physical CPUs and the NUMA node count returns the number of processor nodes (for large cluster computers).

If you're expecting this to run on larger clusters be aware, from the docs :

On systems with more than 64 logical processors, the GetLogicalProcessorInformation function retrieves logical processor information about processors in the processor group to which the calling thread is currently assigned. Use the GetLogicalProcessorInformationEx function to retrieve information about processors in all processor groups on the system.

J...
  • 30,968
  • 6
  • 66
  • 143
  • My Delphi 7 does not recognize ULONG_PTR and ULONGLONG type from your link. ProcessorMask: ULONG_PTR; 3: (Reserved: array [0..1] of ULONGLONG); – Atak_Snajpera Sep 07 '17 at 13:02
  • @Atak_Snajpera I've added the `delphi-7` tag to your question - this is always useful information. Otherwise we have to guess which version you are using. `ULONG_PTR` you can simply change to `Cardinal`. `ULONGLONG` is `Uint64`. – J... Sep 07 '17 at 13:09
  • UInt64 wasn't available in D7, but I believe Int64 will do just as well in this context. – Ken Bourassa Sep 07 '17 at 15:55
  • @KenBourassa I thought it was the other way around (UInt64 but no Int64...). Don't have D7 to hand, unfortunately. – J... Sep 07 '17 at 15:58
  • @J..., Nevermind, I read my reference a bit too fast... UInt64 was available in Delphi 7, but it was merely an alias for Int64 (It was signed). UInt64 only became unsigned starting in D2007. https://stackoverflow.com/questions/36070384/starting-with-which-version-of-delphi-is-uint64-not-just-available-but-also-actu – Ken Bourassa Sep 07 '17 at 16:19