title: WMI values to array
body: <p>Good afternoon guys!</p>
  
  <p>I'm currently developing a small system management tool, very much in a style similar to the Windows 8 Task Manager in <code>Firemonkey</code> (<code>Delphi XE2</code>). At the moment, i'm attempting to create the CPU Core Usage tiles using <code>TPanels</code> inside a <code>TGridLayout</code>. I've added all my panels at designtime until i get the actual functionality sorted.</p>
  
  <p>My issue lies with outputting the data from WMI into an array. I've already had a working version not involving an array, but i'm having trouble creating something more dynamic and less likely to fall over in different configurations. Here's the current code that's not working <em>entirely</em>;</p>
  
  <pre><code>Procedure CoreUsage;
  const
    WbemUser            ='';
    WbemPassword        ='';
    WbemComputer        ='localhost';
    wbemFlagForwardOnly = $00000020;
  var
    FSWbemLocator : OLEVariant;
    FWMIService   : OLEVariant;
    FWbemObjectSet: OLEVariant;
    FWbemObject   : OLEVariant;
    oEnum         : IEnumvariant;
    iValue        : LongWord;
    i : Integer;
  begin
  try
    CoInitialize(nil);
    try
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
      FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_PerfFormattedData_Counters_ProcessorInformation WHERE NOT Name="_Total" AND NOT Name="0,_Total"',
                                             'WQL',
                                             wbemFlagForwardOnly);
      oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
      begin
        if oEnum.Next(1, FWbemObject, iValue) = 0 then
        begin
          for i := 0 to 63 do
          begin
            CoreName[i] := FWbemObject.Name;
            CoreUsage[i] := FWbemObject.PercentProcessorTime;
          end;
        end;
      FWbemObject:=Unassigned;
    end;
  finally
    CoUninitialize;
  end;
  end;
  end;
  </code></pre>
  
  <p><code>CoreUsage</code> and <code>CoreName</code> are both global variable arrays of length <code>[0..63]</code> of the relevant types (CoreName is of <code>String</code>, and CoreUsage is of <code>Uint64</code>). The problem with this is that it shows the same value in all the panels as opposed to a per-core basis (which is my intent). It's almost like it's not going through the 0..63, and instead just retrieving the first value only.</p>
  
  <p>I'm essentially wanting to assign each one to a value within the array as that would allow me to easily read core-specific details without needing to hard code. In the code i had working completely on my development machine, i had to manually go through and use something like this;</p>
  
  <pre><code>if AnsiContainsText(FWbemObject.Name,'3') then
  begin
    CoreName[3] := FWbemObject.Name;
    CoreUsage[3] := FWbemObject.PercentProcessorTime;
  end
  </code></pre>
  
  <p>As you can see, it's hard coded to expect something but would be flaky in systems with 3 CPU's (in the above example). Obviously '3' was replaced with the appropriate array id, all the way from 0 to 63 (though 0 was looking for <code>0,0</code> specifically). The problem with this is that the CPU format is actually of <code>x,y</code> (where <code>x</code> is the CPU and <code>y</code> is the core). It's also not code that i'd be proud to use on the simple basis of it being unreliable. For example, the core name could potentially be <code>0,0 .. 0,7</code> and then <code>1,0 .. 1,7</code> for a system with 2 eight-core CPU's (16 logical cores).</p>
  
  <p>Equally important, the code itself would have been a nightmare to maintain, but could have potentially been shortened down to a single <code>for i :=</code> clause and using <code>IntToStr</code>. But the problem of making sure it works would still remain.</p>
  
  <p>I'm sure there's a perfectly possible way to do this, but i can't figure it out. I did try using;</p>
  
  <p><code>for VarArrayLowBound(FWbemObject.Name, 1) to VarArrayHighBound(FWbemObject.Name, 1) do</code></p>
  
  <p>and</p>
  
  <p><code>for VarArrayLowBound(FWbemObject.PercentProcessorTime, 1) to VarArrayHighBound(FWbemObject.PercentProcessorTime, 1) do</code></p>
  
  <p>but alas, no such luck. Any ideas?</p>