2010-05-03 7 views
12

Estoy usando Delphi 2010, y cuando creé una aplicación de consola que imprime "Hello World", toma 111 kb. Si deseo consultar WMI con Delphi, agrego unidades WBEMScripting_TLB, ActiveX y Variantes a mi proyecto. Si realizo una consulta WMI simple, mi tamaño de ejecutable salta a 810 kb. I¿Cómo uso WMI con Delphi sin aumentar drásticamente el tamaño del archivo de la aplicación?

¿Hay alguna forma de consultar WMI sin una adición tan grande al tamaño del archivo? Perdona mi ignorancia, pero ¿por qué no tengo este problema con C++?

Aquí está mi código:

program WMITest; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, 
    WBEMScripting_TLB, 
    ActiveX, 
    Variants; 

function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string; 
var 
    Services: ISWbemServices; 
    SObject: ISWbemObject; 
    ObjSet: ISWbemObjectSet; 
    SProp: ISWbemProperty; 
    Enum: IEnumVariant; 
    Value: Cardinal; 
    TempObj: OLEVariant; 
    loc: TSWbemLocator; 
    SN: string; 
    i: integer; 
begin 
    Result := ''; 
    i := 0; 
    try 
    loc := TSWbemLocator.Create(nil); 
    Services := Loc.ConnectServer(wmiHost, root {'root\cimv2'}, '', '', '', '', 
     0, nil); 
    ObjSet := Services.ExecQuery('SELECT * FROM ' + wmiClass, 'WQL', 
     wbemFlagReturnImmediately and wbemFlagForwardOnly, nil); 
    Enum := (ObjSet._NewEnum) as IEnumVariant; 
    if not VarIsNull(Enum) then 
     try 
     while Enum.Next(1, TempObj, Value) = S_OK do 
     begin 
      try 
      SObject := IUnknown(TempObj) as ISWBemObject; 
      except SObject := nil; 
      end; 
      TempObj := Unassigned; 
      if SObject <> nil then 
      begin 
      SProp := SObject.Properties_.Item(wmiProperty, 0); 
      SN := SProp.Get_Value; 
      if not VarIsNull(SN) then 
      begin 
       if varisarray(SN) then 
       begin 
       for i := vararraylowbound(SN, 1) to vararrayhighbound(SN, 1) do 
        result := vartostr(SN[i]); 
       end 
       else 
       Result := SN; 
       Break; 
      end; 
      end; 
     end; 
     SProp := nil; 
     except 
     Result := ''; 
     end 
    else 
     Result := ''; 
    Enum := nil; 
    Services := nil; 
    ObjSet := nil; 
    except 
    on E: Exception do 
     Result := e.message; 
    end; 
end; 

begin 
    try 
    WriteLn('hello world'); 
    WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem', 
     'Caption')); 
    WriteLn('done'); 

    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 

ACTUALIZACIÓN: Cuando compilo el following sample from MSDN con Microsoft 2008 (aplicación de consola) Visual C++, que es de 76 kb.

+0

Cuando dices "con C++", ¿qué compilador de C++ estás usando? C++ Builder? Visual C++? GCC? ¿Qué versión? –

+4

¿Por qué es "una aplicación de consola que imprime 'Hola mundo'" algún tipo de punto de datos significativo para el tamaño del archivo ejecutable? –

+0

Es un marco de referencia para usuarios que no pertenecen a Delphi que pueden estar familiarizados con la resolución del problema usando C/C++. – Mick

Respuesta

26

@Mick, puede acceder al WMI sin importar el WBEMScripting de Delphi, utilizando las interfaces IBindCtx y IMoniker.

Compruebe este código simple (Probado en Delphi 2010 y Windows 7), el tamaño del archivo exe es 174 kb.

program WmiTest; 

{$APPTYPE CONSOLE} 


uses 
    SysUtils 
    ,ActiveX 
    ,ComObj 
    ,Variants; 


function GetWMIstring(wmiHost, root, wmiClass, wmiProperty: string): string; 
var 
    objWMIService : OLEVariant; 
    colItems  : OLEVariant; 
    colItem  : OLEVariant; 
    oEnum   : IEnumvariant; 
    iValue  : LongWord; 

    function GetWMIObject(const objectName: String): IDispatch; 
    var 
    chEaten: Integer; 
    BindCtx: IBindCtx;//for access to a bind context 
    Moniker: IMoniker;//Enables you to use a moniker object 
    begin 
    OleCheck(CreateBindCtx(0, bindCtx)); 
    OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));//Converts a string into a moniker that identifies the object named by the string 
    OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));//Binds to the specified object 
    end; 

begin 
    objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',[wmiHost,root])); 
    colItems  := objWMIService.ExecQuery(Format('SELECT * FROM %s',[wmiClass]),'WQL',0); 
    oEnum   := IUnknown(colItems._NewEnum) as IEnumVariant; 
    while oEnum.Next(1, colItem, iValue) = 0 do 
    begin 
    Result:=colItem.Properties_.Item(wmiProperty, 0); //you can improve this code ;) , storing the results in an TString. 
    end; 
end; 

begin 
try 
    CoInitialize(nil); 
    try   
     WriteLn(GetWMIstring('.', 'root\CIMV2', 'Win32_OperatingSystem','Caption')); 
     Readln; 
    finally 
    CoUninitialize; 
    end; 
except 
    on E:Exception do 
    Begin 
     Writeln(E.Classname, ': ', E.Message); 
     Readln; 
    End; 
    end; 
end. 
+0

¡Esto es * exactamente * lo que estaba buscando! – Mick

+0

tengo ** no ** idea de cómo funciona esto, ¡pero me gusta! –

+2

@IanBoyd verifique este artículo [Cómo acceder al WMI desde Object Pascal Code (Delphi, Oxygene, FreePascal)] (http://theroadtodelphi.wordpress.com/2010/12/01/accesing-the-wmi-from-pascal-code -delphi-oxygene-freepascal /) – RRUZ

0

Bueno, no sé sobre WBEMScripting_TLB, pero ActiveX.pas es una unidad bastante grande. Son casi 7000 líneas en mi instalación D2010. Si tiene que incluir una cantidad significativa de eso en su código, entonces puede esperar que agregue unos pocos cientos de K a su tamaño EXE.

¿Qué tan grande es el TLB, por cierto?

6

ActiveX y/o variantes agregarían 36 KB como máximo.
Es WBEMScripting_TLB que añade sobre 650KB a su proyecto.
No es enorme en líneas de código, pero más que declarar bastantes clases, interfaces y constantes, es incluye OleServer en sus usos.
Y que trae toda la unidadControls con su equipaje pesado.

+0

Es cierto. Por otra parte, esto ya no es "equipaje pesado". Diablos, ¡todo el ejecutable "drásticamente aumentado" todavía cabría en un disquete de 1,44 MB! –

+1

Viéndolo de esa manera ... Aún así, es un aumento del factor 7, y ya no cabría en el disquete de 360K, los que realmente se saltan. :-) –

2

cuando Delphi construye un ejecutable, enlaza estáticamente en las bibliotecas delphi runtime. esto da como resultado un archivo ejecutable más grande, sin embargo, como el rtl está vinculado estáticamente, la implementación es más fácil y hay un elemento de pruebas en el futuro.

puede configurar delphi para usar paquetes de tiempo de ejecución habilitando Build with runtime packages en el Project/Options. sin embargo, tendrá que asegurarse de que los paquetes delphi rtl estén disponibles, y puede encontrar problemas con la depuración.

este comportamiento de vinculación estático frente a tiempo de ejecución probablemente explique las diferencias que está viendo entre delphi y C++.

1

La diferencia que está viendo, en parte de todos modos, se debe a que VC++ utiliza bibliotecas de tiempo de ejecución dinámicamente vinculadas por defecto; las bibliotecas de tiempo de ejecución se cargan desde archivos DLL cuando se ejecuta la aplicación y, por lo tanto, el código no está presente en el archivo ejecutable.

Delphi, OTOH, por defecto enlaces en todo el código de la biblioteca de tiempo de ejecución a menos que construya con paquetes de tiempo de ejecución habilitados. Esta diferencia en las configuraciones predeterminadas representará la mayoría de las diferencias de tamaño entre los archivos ejecutables.

Cuestiones relacionadas