2012-04-17 78 views
13

我写了一个简单的函数来使用WMI检索系统信息,并将参数传递给类和属性名称。当我执行这样的功能时如何使用delphi提高WMI性能?

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

执行时间大约是1300毫秒。

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

这与功能

{$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. 

回答

19

这些都是一些提示,以提高性能的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毫秒去(我的笔记本电脑)。

+6

+1。很好,而且你让我删除了我低级的答案。 :)除了你所说的,海报可能要看看MagWMI,这是一套免费的Delphi封装,让事情变得更简单。它在内部做一些信息的缓存,使得多个查询更容易。该包装是免费的,并附带一个非常广泛的演示应用程序,可能会有所帮助。 – 2012-04-17 22:03:22

+2

是的,这与我所说的几乎是一回事......但是我个人认为更好的一般模式是将缓存服务变量传递给函数,这允许DI和模拟测试框架中的变量。 – 2012-04-17 22:17:34

+0

当然更好的是创建一个对象来封装连接和函数。这个示例应用程序只是一个概念证明。此外,点3从未被提及并且非常重要。 – RRUZ 2012-04-17 22:22:24

4

这是一般的经验规则示例应用程序。

当你说你想要检索很多附加信息时,我假设这意味着你可能会在很多循环中调用这个函数。对于性能调优,您只需要花费大量时间并且可以重复使用的那些东西,即循环即缓存它们。

在这种情况下,CreateOleObject很可能会花费你大部分时间,因为第一遍我会把它放在循环(或多个调用)之外并将sWebLocator传递到函数中,作为第二次传递可能希望将ConnectServer调用从该函数中取出,并将sWMIService对象也传入。