14

我编写了一个简单的函数来使用 WMI 检索系统信息,将类和属性名称作为参数传递。当我执行这样的功能时

  Writeln('Procesor Id '+GetWMIInfo('Win32_Processor','Name'));
  Writeln('Mother Board Serial '+GetWMIInfo('Win32_BaseBoard','SerialNumber'));
  Writeln('BIOS Version '+GetWMIInfo('Win32_BIOS','Version'));

执行时间约为 1300 ms。

我需要检索很多附加信息,那么是否可以减少此函数的执行时间?

这是一个带有函数的示例应用程序

{$APPTYPE CONSOLE}

uses
  Diagnostics,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

function  GetWMIInfo(const WMIClass, WMIProperty:string): string;
var
  sWbemLocator  : OLEVariant;
  sWMIService   : OLEVariant;
  sWbemObjectSet: OLEVariant;
  sWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  Result:='';
  sWbemLocator  := CreateOleObject('WbemScripting.SWbemLocator');
  sWMIService   := sWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
  sWbemObjectSet:= sWMIService.ExecQuery('SELECT * FROM '+WMIClass,'WQL');
  oEnum         := IUnknown(sWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, sWbemObject, iValue) = 0 then
    Result:=sWbemObject.Properties_.Item(WMIProperty).Value;
end;

var
 SW : TStopwatch;

begin
 try
    CoInitialize(nil);
    try
      SW.Reset;
      SW.Start;
      Writeln('Procesor Id '+GetWMIInfo('Win32_Processor','Name'));
      Writeln('Mother Board Serial '+GetWMIInfo('Win32_BaseBoard','SerialNumber'));
      Writeln('BIOS Version '+GetWMIInfo('Win32_BIOS','Version'));
      SW.Stop;
      Writeln('Elapsed ms '+FormatFloat('#,0.000',SW.Elapsed.TotalMilliseconds));
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Readln;
end.
4

2 回答 2

21

这些是提高 WMI 性能的一些技巧

1.) 重新调用CreateOleObject

2.) 重用 WMI 连接

更昂贵的任务之一是与 WMI 服务建立连接,因此请重新利用该连接,而不是每次调用该函数时都创建一个连接。

3.) 仅检索您要使用的列

检索 WMI 的每个属性都有不同的来源,如 Windows 注册表、WinAPi 等,限制列将提高性能。阅读本文了解更多信息How obtain the source of the WMI Data

4.)在执行 WQL 语句时使用WBEM_FLAG_FORWARD_ONLY标志。

按照上述提示,我重写了您的示例应用程序

{$APPTYPE CONSOLE}

uses
  Diagnostics,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;

function  GetWMIInfo(const WMIClass, WMIProperty:string): string;
const
  wbemFlagForwardOnly = $00000020;
var
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  Result:='';
  FWbemObjectSet:= FWMIService.ExecQuery(Format('Select %s from %s',[WMIProperty, WMIClass]),'WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
    Result:=FWbemObject.Properties_.Item(WMIProperty).Value;
end;

var
 SW : TStopwatch;

begin
 try
    CoInitialize(nil);
    try
      SW.Reset;
      SW.Start;
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
      Writeln('Procesor Id '+GetWMIInfo('Win32_Processor','Name'));
      Writeln('Mother Board Serial '+GetWMIInfo('Win32_BaseBoard','SerialNumber'));
      Writeln('BIOS Version '+GetWMIInfo('Win32_BIOS','Version'));
      SW.Stop;
      Writeln('Elapsed ms '+FormatFloat('#,0.000',SW.Elapsed.TotalMilliseconds));
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Readln;
end.

执行时间从 1245 到 180 毫秒(在我的笔记本电脑上)。

于 2012-04-17T22:01:35.097 回答
4

这是一般的经验法则。

当您说要检索大量附加信息时,我认为这意味着您将多次调用此函数,可能是在循环中。对于性能调整,您只需将那些花费大量时间并且可以重用的东西从循环中取出,即缓存它们。

在这种情况下, CreateOleObject 可能会花费您大部分时间,作为第一遍,我会将其放在循环之外(或多次调用)并将您的 sWebLocator 传递给函数,作为您可能想要的第二遍ConnectServer 也调用函数并将 sWMIService 对象也传入。

于 2012-04-17T21:58:41.320 回答