2012-04-17 9 views
13

Escribí una función simple para recuperar información del sistema usando WMI, pasando como parámetro la clase y el nombre de la propiedad. cuando ejecuto la función de esta manera¿Cómo puedo mejorar el rendimiento de WMI usando Delphi?

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

El tiempo de ejecución es de aproximadamente 1300 ms.

Necesito recuperar mucha información adicional, ¿Es posible reducir el tiempo de ejecución de esta función?

Esta es una aplicación de ejemplo con la función

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

Respuesta

19

Estos son algunos consejos para mejorar el rendimiento de WMI

1.) reutilizar la llamada a CreateOleObject

2.) reutilizar la conexión WMI

Una de las tareas más costosas es establecer una conexión con los servicios WMI, así que reutilice esa conexión en lugar de crear una conexión cada vez que llame a la función.

3.) recuperar sólo las columnas que desea utilizar

Cada propiedad que recuperar el WMI tiene diferentes fuentes como el registro de Windows, la API de Windows y así sucesivamente, lo que restringe las columnas mejorará el rendimiento. lee este artículo para más información How obtain the source of the WMI Data

4.) Usa la bandera WBEM_FLAG_FORWARD_ONLY cuando ejecutas la sentencia WQL.

Siguiendo los consejos anteriores Reescribí su aplicación de ejemplo

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

Y la ejecución pasa de 1.245 a 180 ms (en mi portátil).

+6

+1. Bien, y otra vez me haces eliminar mi respuesta inferior. :) Además de lo que dices, el póster podría querer ver MagWMI, un conjunto gratuito de envolturas Delphi que facilitan las cosas. Internamente hace un poco de almacenamiento en caché de la información que hace que las consultas múltiples sean mucho más fáciles. El contenedor es gratuito y viene con una aplicación de demostración realmente extensa que podría ser útil. –

+2

sí, esto es más o menos lo mismo que decía ... sin embargo, personalmente creo que un mejor patrón general es pasar los vars de servicio en caché a la función, esto permite que DI y burlarse de esos vars en un marco de prueba . –

+0

Por supuesto, mejor es crear un objeto para encapsular la conexión y la función. esta aplicación de muestra es solo una prueba de concepto. Además, el punto 3 nunca se menciona y es muy importante. – RRUZ

4

Ésta es una regla general.

Cuando dices que quieres recuperar mucha información adicional, supongo que significa que llamarás mucho a esta función, posiblemente en un bucle. Para la optimización del rendimiento, simplemente necesita tomar aquellas cosas que cuestan mucho tiempo y que puede reutilizar, fuera del ciclo, es decir, almacenarlas en caché.

En este caso, CreateOleObject es probable que le cueste la mayor parte del tiempo, como primer paso lo pondría fuera del bucle (o varias llamadas) y pasar su sWebLocator en la función, como un segundo pase también podría eliminar la función ConnectServer de la función y pasar el objeto sWMIService también.

Cuestiones relacionadas