The Road to Delphi

Delphi – Free Pascal – Oxygene


2 Comments

WMI Delphi Code Creator – New features and source code published in google code

Update : The WMI Delphi Code Creator now is hosted on Github.

In the last months I’ been working in a new version of the WMI Delphi Code Creator, porting the original code from Delphi 2007 to XE, adding new features like support for Delphi Prism and Free Pascal, and improving the source code generated by the tool. Now is time to show the results, so the new version is here with many new features,  also  the full source code is now available in the google code project hosting site under the Mozilla Public License 1.1

 

Check the list of features of the current version

  • Can generate object pascal code compatible with one of these compilers Delphi Win32, Delphi -Prism (Oxygene) , Free Pascal
  • The Delphi code generated is compatible with  Delphi 7, 2005, BDS/Turbo 2006 and RAD Studio 2007, 2009, 2010, XE.
  • The Free Pascal code generated is compatible with these versions 2.4.2, 2.4.4
  • The Delphi prism .Net  (Oxygene) generated code is compatible with all the versions up to 4.0.23.741 (in newer versions must work too)
  • Full access to metadata of any WMI Class registered in the system including qualifiers, mof definition, properties, methods, events
  • You can access directly from the application the MSDN web page related to the WMI Class which your are using.
  • Compile and run the generated code directly form the application using the selected compiler.
  • Open the the generated Delphi code in any of these  Delphi IDE’s 7, 2005, BDS/Turbo 2006 and RAD Studio 2007, 2009, 2010, XE
  • Open the the generated Free Pascal code directly in the Lazarus IDE.
  • Open the the generated Delphi Prism code directly in Visual Studio 2008, Visual Studio 2010 or MonoDevelop.
  • Runs on Windows XP, 2003, 2008, Vista and 7.
  • Themes support for Syntax highlighting (+50 themes included) compatible with the Delphi IDE Theme Editor.

For download the new version of the application and more details go to project page


1 Comment

Microsoft SQL Server CLR and Delphi Prism Part 1 : CLR Stored Procedures

This is the first of a series of articles where i will show you how integrate Delphi Prism and the SQL Server CLR support.

SQL Server (Since version 2005) allow you to create database objects using the integration with the .NET Framework common language runtime (CLR). this mean which you can create a .net assembly and then call the methods defined in your class directly from SQL Server like any SQL Object, you can create Scalar-valued user-defined functions (scalar UDFs), Table-valued user-defined functions (TVFs), User-defined procedures (UDPs) and User-defined triggers.

today we will see how to create CLR Stored Procedures from Delphi prism.

Ok, first you need to create a new Delphi prism project of class library type.

Now before to define the code to be accessed from SQL Server you must know the requirements of the CLR stored procedures.

1) In the common language runtime (CLR), stored procedures are implemented as public static methods on a class in a Microsoft .NET Framework assembly.

2) you must include the Microsoft.SqlServer.Server namespace in your uses list.

3) The Parameters passed to a CLR stored procedure can be any of the native SQL Server types that have an equivalent in managed code

4) all the methods must have the [Microsoft.SqlServer.Server.SqlProcedure()] attribute, this attribute tells to Visual Studio compiler that this is a stored procedure for SQL Server.

Check this sample declaration for a Delphi prism class which contains CLR stored procedures.

namespace MyNameSpace;

interface
uses
  System,
  System.Data,
  System.Data.SqlClient,
  Microsoft.SqlServer.Server,
  System.Data.SqlTypes;

type
  MyClass  = public class
  public
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetSqlVersion(out Intvalue: SqlInt32;out Strvalue: SqlString);
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method Hello;
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetTables;
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetRecordResult;
  end;

The CLR Stored Procedures can return information in several ways. This includes output parameters, tabular results, and messages.

OUTPUT Parameters and CLR Stored Procedures
in this sample method you can see how can return output parameters. this method return the sql version string and the sql version as a integer value. (off course you can obtain directly from SQL Server this information but this is just a sample)

class method MyClass.GetSqlVersion(out Intvalue: SqlInt32;out Strvalue: SqlString); //look the types used, are SQL Server types remember the requirements?
begin
    Intvalue := 0;
    Strvalue := '';    
    var  connection: SqlConnection := new SqlConnection('context connection=true'); //this is cool in this line you are using the current connection instance of the caller client, so you don't need connect to the SQL server.
    connection.Open();
    var command: SqlCommand := new SqlCommand('Select @@VERSION,@@MICROSOFTVERSION', connection); //get the version info
    var reader: SqlDataReader := command.ExecuteReader(); 
      if reader.Read() then begin
        Strvalue := reader.GetSqlString(0); //assign the values
        Intvalue := reader.GetSqlInt32(1);
      end
end;

Returning Messages
one of the overload versions of the SqlContext.Pipe.Send method allow you to send messages directly to the client or current output consumer.

class method MyClass.Hello;
begin
     SqlContext.Pipe.Send('Hello From delphi Prism'); //send the message to client
end;

Returning Tabular Results directly to the client
You can return directly to the client a result set. this sample method returns a list of the tables and rows in the current database.

class method MyClass.GetTables;
begin 
    var  connection: SqlConnection := new SqlConnection('context connection=true');
    connection.Open();
    var command: SqlCommand := new SqlCommand(
    'SELECT  so.name [TableName],MAX(si.rows) [RowCount] '+
    'FROM '+ 
    'sysobjects so, '+ 
    'sysindexes si '+ 
    'WHERE '+ 
    'so.xtype = ''U'' '+ 
    'AND '+ 
    'si.id = OBJECT_ID(so.name) '+ 
    'GROUP BY '+ 
    'so.name '+ 
    'ORDER BY '+ 
    '2 DESC', connection);
     // send the result set to the client directly.
     SqlContext.Pipe.ExecuteAndSend(command);   
end;

Creating a dynamic result set and send them to the client
You can also create a result set on the fly and return to the client the results. this sample method create a record with 4 columns(fields).

class method MyClass.GetRecordResult;
begin
      // Create a record object with 4 columns that represents an individual row, including it's metadata.
      var ARecord : SqlDataRecord  := new SqlDataRecord(new SqlMetaData('Col1', SqlDbType.NVarChar, 100),
                                                        new SqlMetaData('Col2', SqlDbType.Int),
                                                        new SqlMetaData('Col3', SqlDbType.Float),
                                                        new SqlMetaData('Col4', SqlDbType.DateTime)
      );    
      // Populate the record.
      ARecord.SetString(0, 'Delphi Prism is cool');      
      ARecord.SetInt32 (1, 1024*1024);      
      ARecord.SetDouble(2, Math.PI);      
      ARecord.SetDateTime(3, DateTime.Now);    
      // Send the record to the client.
      SqlContext.Pipe.Send(ARecord);
end;

Ok, now we have our code, we need deploy in the SQL Server.

first you must activate CLR support in the SQL server, because is disabled by default. execute this sentence to activate the CLR support.

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

Now we need register our assembly using the CREATE ASSEMBLY sentence.
this is the syntax

CREATE ASSEMBLY assembly_name
[ AUTHORIZATION owner_name ]
FROM { <client_assembly_specifier> | <assembly_bits> [ ,...n ] }
[ WITH PERMISSION_SET = { SAFE | EXTERNAL_ACCESS | UNSAFE } ]
[ ; ]
<client_assembly_specifier> :: ='[\\computer_name\]share_name\[path\]manifest_file_name'
  | '[local_path\]manifest_file_name'<assembly_bits> :: =
{ varbinary_literal | varbinary_expression }

in this case the sentence will look like this

CREATE ASSEMBLY MyDelphiPrismAssembly1 from 'C:\Prism Projects\Delphi Prism SqlCLR\MyPrismClr\bin\Release\MyPrismClr.dll' 
WITH PERMISSION_SET = SAFE
GO

if all it’s ok you must can view the registered assembly under the assemblies folder of your SQL Management Studio.

next we need create and register every procedure of our assembly. see this sample, in this sentence we are creating a SQL Server stored procedure called hello where the implementation is defined in our assembly.

CREATE PROCEDURE Hello
AS
EXTERNAL NAME MyDelphiPrismAssembly1.[MyNameSpace.MyClass].Hello

now you can execute this stored procedure in this way

 EXEC Hello

and the result will be

Now the same steps to register our GetTables method

CREATE PROCEDURE GetTables
AS
EXTERNAL NAME MyDelphiPrismAssembly1.[MyNameSpace.MyClass].GetTables

and when you execute this stored procedure the result will be something like this.

now creating the GetRecordResult stored procedure

CREATE PROCEDURE GetRecordResult
AS
EXTERNAL NAME MyDelphiPrismAssembly1.[MyNameSpace.MyClass].GetRecordResult

and the output

finally this is how you must register a stored procedure with output parameters.

CREATE PROCEDURE  GetSqlVersion(@Intvalue int OUTPUT, @Strvalue NVARCHAR(255) OUTPUT)
AS
EXTERNAL NAME MyDelphiPrismAssembly1.[MyNameSpace.MyClass].GetSqlVersion

and to see the results

DECLARE @Intvalue int
DECLARE @Strvalue NVARCHAR(255)
EXECUTE  GetSqlVersion @Intvalue OUTPUT , @Strvalue OUTPUT
SELECT @Intvalue Version_Number,@Strvalue Version_Str

This is the full source-code of the Delphi prism class used in this article.

namespace MyNameSpace;

interface
uses
  System,
  System.Data,
  System.Data.SqlClient,
  Microsoft.SqlServer.Server,
  System.Data.SqlTypes;

type
  MyClass  = public class
  public
    //OUTPUT Parameters and CLR Stored Procedures
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetSqlVersion(out Intvalue: SqlInt32;out Strvalue: SqlString);
    //Returning Messages
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method Hello;
    //Returning Tabular Results directly to the client
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetTables;
    //Creating a dynamic result set and send them to the client
    [Microsoft.SqlServer.Server.SqlProcedure()]
    class method GetRecordResult;
  end;
  
implementation

//OUTPUT Parameters and CLR Stored Procedures
class method MyClass.GetSqlVersion(out Intvalue: SqlInt32;out Strvalue: SqlString);
begin
    Intvalue := 0;
    Strvalue := '';    
    var  connection: SqlConnection := new SqlConnection('context connection=true');
    connection.Open();
    var command: SqlCommand := new SqlCommand('Select @@VERSION,@@MICROSOFTVERSION', connection);
    var reader: SqlDataReader := command.ExecuteReader();
      if reader.Read() then begin
        Strvalue := reader.GetSqlString(0);
        Intvalue := reader.GetSqlInt32(1);
      end
end;

//Returning Messages
class method MyClass.Hello;
begin
     SqlContext.Pipe.Send('Hello From delphi Prism');
end;

//Returning Tabular Results directly to the client
class method MyClass.GetTables;
begin 
    var  connection: SqlConnection := new SqlConnection('context connection=true');
    connection.Open();
    var command: SqlCommand := new SqlCommand(
    'SELECT  so.name [TableName],MAX(si.rows) [RowCount] '+
    'FROM '+ 
    'sysobjects so, '+ 
    'sysindexes si '+ 
    'WHERE '+ 
    'so.xtype = ''U'' '+ 
    'AND '+ 
    'si.id = OBJECT_ID(so.name) '+ 
    'GROUP BY '+ 
    'so.name '+ 
    'ORDER BY '+ 
    '2 DESC', connection);
     // send the result set to the client directly.
     SqlContext.Pipe.ExecuteAndSend(command);   
end;

class method MyClass.GetRecordResult;
begin
      // Create a record object that represents an individual row, including it's metadata.
      var ARecord : SqlDataRecord  := new SqlDataRecord(new SqlMetaData('Col1', SqlDbType.NVarChar, 100),
                                                        new SqlMetaData('Col2', SqlDbType.Int),
                                                        new SqlMetaData('Col3', SqlDbType.Float),
                                                        new SqlMetaData('Col4', SqlDbType.DateTime)
      );    
      // Populate the record.
      ARecord.SetString(0, 'Delphi Prism is cool');      
      ARecord.SetInt32 (1, 1024*1024);      
      ARecord.SetDouble(2, Math.PI);      
      ARecord.SetDateTime(3, DateTime.Now);      
      // Send the record to the client.
      SqlContext.Pipe.Send(ARecord);
end;


end.

the samples showed in this article are very simple, and just pretend show the basic functionalities, you can find many ways to apply this to your own project. personally I use SQL CLR in real life applications to calculate hashes, encrypt data and make complex calculations.

See you.


13 Comments

Accesing the WMI from Object Pascal Code (Delphi, Oxygene, FreePascal)

Due to many developers don’t know how access the WMI from Pascal code, I decided to write this entry to show a simple set of samples using Delphi Win32, Delphi.Net, Oxygene and Free-pascal.

Delphi .Net

Delphi Prism

Delphi win32 importing the Microsoft WMIScripting Library.

Delphi win32 Late-binding.

Lazarus Late-binding

UPDATE

To access the WMI using COM  you must check this article

Accesing the WMI from Delphi and FPC via COM (without late binding or WbemScripting_TLB)

All the code samples uses the Win32_BaseBoard wmi class and are console applications just for simplicity.

The code showed in this entry can be optimized a lot for experienced developers, these simple  piece of code only pretend give a basic idea which how connect to the  WMi service and retieve data using a WQL query.

This is the Managed Object Format (MOF) representation of this class.

class Win32_BaseBoard : CIM_Card
{
  string   Caption;
  string   ConfigOptions[];
  string   CreationClassName;
  real32   Depth;
  string   Description;
  real32   Height;
  boolean  HostingBoard;
  boolean  HotSwappable;
  datetime InstallDate;
  string   Manufacturer;
  string   Model;
  string   Name;
  string   OtherIdentifyingInfo;
  string   PartNumber;
  boolean  PoweredOn;
  string   Product;
  boolean  Removable;
  boolean  Replaceable;
  string   RequirementsDescription;
  boolean  RequiresDaughterBoard;
  string   SerialNumber;
  string   SKU;
  string   SlotLayout;
  boolean  SpecialRequirements;
  string   Status;
  string   Tag;
  string   Version;
  real32   Weight;
  real32   Width;
};

Using Delphi Win32 and importing the Microsoft WMIScripting Library

This must be the most used method for accessing the WMI from Delphi Win32, basically consist in import the Microsoft WMIScripting Library and then delphi will create a wrapper containing all the types, const and enumerations to access the WMI, the main objects(interfaces) are ISWbemServices (which create a connection with WMI Windows Service) , ISWbemObjectSet (Execute a WQL Query) and ISWbemPropertySet

Check the picture to see the Microsoft Wmi Scripting library organization.

The basics steps are

1) import the Microsoft WMIScripting Library

2) Establish a connection with the WMI service in a local o remote computer. using the TSWbemLocator.ConnectServer function.

    function ConnectServer(const strServer: WideString; const strNamespace: WideString;
                           const strUser: WideString; const strPassword: WideString;
                           const strLocale: WideString; const strAuthority: WideString;
                           iSecurityFlags: Integer; const objWbemNamedValueSet: IDispatch): ISWbemServices;

 

SWbemServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);

3) Execute your WQL Query using the ISWbemServices.ExecQuery function which returns a instance to the ISWbemObjectSet object.

this is the declaration of the ISWbemServices.ExecQuery

    function ExecQuery(const strQuery: WideString; const strQueryLanguage: WideString;
                       iFlags: Integer; const objWbemNamedValueSet: IDispatch): ISWbemObjectSet; safecall;

and this is a sample of calling this function

SWbemObjectSet  := SWbemServices.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL', 0, nil);

4) the next step is iterate over the returned data, to do this we can use the ISWbemObjectSet._NewEnum property wich return an enumerator to the collection returned by the SWbemObjectSet

so using a IEnumVariant (which is part of the ActiveX unit) variable we can obtain the enumerator to the collection in this way.

Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;

5) finally we need access the properties of the current collection, using the SWbemObject.Properties_.Item function

    while (Enum.Next(1, TempObj, Value) = S_OK) do
    begin
       SWbemObject     := IUnknown(tempObj) as ISWBemObject;
       SWbemPropertySet:= SWbemObject.Properties_;
       SWbemPropertySet.Item('SerialNumber', 0).Get_Value;//Get the value of the SerialNumber property
    end;

Advantages of importing the Microsoft WMIScripting Library

1) Full access through the IDE (code-completion) to the types, enumerations and constants of the WMIScripting Library.
2) easy to debug syntax errors which would have been missed had you used late binding.

Drawbacks

1) Depending of the Windows version which you uses, you can get different results when you import the WMIScripting Library, check this link.

2) The final exe size is incremented when you import this library.

Check the full sample source code to get a idea how use this method.

program WmiDelphiWin32_Tlb;

{$APPTYPE CONSOLE}

uses
  ActiveX,
  Variants,
  SysUtils,
  WbemScripting_TLB in '..\..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';//

 function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

Procedure GetWin32_BaseBoardInfo;
var
  SWbemServices   : ISWbemServices;
  SWbemObjectSet  : ISWbemObjectSet;
  Item            : Variant;
  Enum            : IEnumVariant;
  TempObj         : OleVariant;
  Value           : Cardinal;
  SWbemObject     : ISWbemObject;
  SWbemPropertySet: ISWbemPropertySet;
begin
  SWbemServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
  SWbemObjectSet  := SWbemServices.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL', 0, nil);
  Enum := (SWbemObjectSet._NewEnum) as IEnumVariant;

    while (Enum.Next(1, TempObj, Value) = S_OK) do
    begin
       SWbemObject     := IUnknown(tempObj) as ISWBemObject;
       SWbemPropertySet:= SWbemObject.Properties_;

        Writeln(Format('Caption                   %s',[VarStrNull(SWbemPropertySet.Item('Caption', 0).Get_Value)]));// String
        Writeln(Format('ConfigOptions             %s',[VarStrNull(SWbemPropertySet.Item('ConfigOptions', 0).Get_Value)]));// String
        Writeln(Format('CreationClassName         %s',[VarStrNull(SWbemPropertySet.Item('CreationClassName', 0).Get_Value)]));// String
        Writeln(Format('Depth                     %s',[VarStrNull(SWbemPropertySet.Item('Depth', 0).Get_Value)]));// Real32
        Writeln(Format('Description               %s',[VarStrNull(SWbemPropertySet.Item('Description', 0).Get_Value)]));// String
        Writeln(Format('Height                    %s',[VarStrNull(SWbemPropertySet.Item('Height', 0).Get_Value)]));// Real32
        Writeln(Format('HostingBoard              %s',[VarStrNull(SWbemPropertySet.Item('HostingBoard', 0).Get_Value)]));// Boolean
        Writeln(Format('HotSwappable              %s',[VarStrNull(SWbemPropertySet.Item('HotSwappable', 0).Get_Value)]));// Boolean
        Writeln(Format('InstallDate               %s',[VarStrNull(SWbemPropertySet.Item('InstallDate', 0).Get_Value)]));// Datetime
        Writeln(Format('Manufacturer              %s',[VarStrNull(SWbemPropertySet.Item('Manufacturer', 0).Get_Value)]));// String
        Writeln(Format('Model                     %s',[VarStrNull(SWbemPropertySet.Item('Model', 0).Get_Value)]));// String
        Writeln(Format('Name                      %s',[VarStrNull(SWbemPropertySet.Item('Name', 0).Get_Value)]));// String
        Writeln(Format('OtherIdentifyingInfo      %s',[VarStrNull(SWbemPropertySet.Item('OtherIdentifyingInfo', 0).Get_Value)]));// String
        Writeln(Format('PartNumber                %s',[VarStrNull(SWbemPropertySet.Item('PartNumber', 0).Get_Value)]));// String
        Writeln(Format('PoweredOn                 %s',[VarStrNull(SWbemPropertySet.Item('PoweredOn', 0).Get_Value)]));// Boolean
        Writeln(Format('Product                   %s',[VarStrNull(SWbemPropertySet.Item('Product', 0).Get_Value)]));// String
        Writeln(Format('Removable                 %s',[VarStrNull(SWbemPropertySet.Item('Removable', 0).Get_Value)]));// Boolean
        Writeln(Format('Replaceable               %s',[VarStrNull(SWbemPropertySet.Item('Replaceable', 0).Get_Value)]));// Boolean
        Writeln(Format('RequirementsDescription   %s',[VarStrNull(SWbemPropertySet.Item('RequirementsDescription', 0).Get_Value)]));// String
        Writeln(Format('RequiresDaughterBoard     %s',[VarStrNull(SWbemPropertySet.Item('RequiresDaughterBoard', 0).Get_Value)]));// Boolean
        Writeln(Format('SerialNumber              %s',[VarStrNull(SWbemPropertySet.Item('SerialNumber', 0).Get_Value)]));// String
        Writeln(Format('SKU                       %s',[VarStrNull(SWbemPropertySet.Item('SKU', 0).Get_Value)]));// String
        Writeln(Format('SlotLayout                %s',[VarStrNull(SWbemPropertySet.Item('SlotLayout', 0).Get_Value)]));// String
        Writeln(Format('SpecialRequirements       %s',[VarStrNull(SWbemPropertySet.Item('SpecialRequirements', 0).Get_Value)]));// Boolean
        Writeln(Format('Status                    %s',[VarStrNull(SWbemPropertySet.Item('Status', 0).Get_Value)]));// String
        Writeln(Format('Tag                       %s',[VarStrNull(SWbemPropertySet.Item('Tag', 0).Get_Value)]));// String
        Writeln(Format('Version                   %s',[VarStrNull(SWbemPropertySet.Item('Version', 0).Get_Value)]));// String
        Writeln(Format('Weight                    %s',[VarStrNull(SWbemPropertySet.Item('Weight', 0).Get_Value)]));// Real32
        Writeln(Format('Width                     %s',[VarStrNull(SWbemPropertySet.Item('Width', 0).Get_Value)]));// Real32
        Writeln('');
        TempObj:=Unassigned;
    end;

end;

begin
 try
    CoInitialize(nil);
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

Using Delphi and Late-Binding

Using the CreateOleObject function

You can access the WMI using the CreateOleObject function from delphi passing the WbemScripting.SWbemLocator class name

  var
     FSWbemLocator : OLEVariant;
  begin
    FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
    //
  end;

And then to run a WQL sentence

const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  //create an instance to the WMI Scripting SWbemLocator
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //connect to the server
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  //execute the WQL sentence
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',wbemFlagForwardOnly);
  //get the enumerator
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  //traverse the data
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    Writeln(Format('Caption    %s',[FWbemObject.Caption]));// String
    Writeln('');
    FWbemObject:=Unassigned;
  end;
end;

Using a Moniker

Another way to access the WMI is using a Moniker, to do this we need to use the IBindCtx and IMoniker interfaces.

Check this sample to create a wmi instance using these interfaces.

  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;

//and call in this way
GetWMIObject('winmgmts:\\localhost\root\CIMV2');

Now we can run the query an get the results

See the snippet, is very similar to the used when we import the WbemScripting_TLB unit. but the main difference is which all the variables are declarated as Variants (OLEVariant) because we don’t have available the types, enumeratios and constants of the WbemScripting_TLB unit.

var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;

3) finally to access a particular property we can get the value directly using his name.

 while oEnum.Next(1, colItem, iValue) = 0 do
 begin
     Writeln('Serial Number : '+colItem.SerialNumber);
     colItem:=Unassigned; //avoid memory leaks caused by the oEnum.Next function
 end;

Advantages of Late Binding

1. Is a very flexible solution to access the WMI, and the code to manage the wmi objects is very close to vbscript, which is good because there are thousands of examples of vbscript on the Internet to access the WMI Data.

2. Another very important advantage is that code which uses late binding is more certain to be version-independent, because when you create a WMIObject using winmgmts you are not referencing any particular version of the WMI.

3. The final exe executable is small because you don’t import any wrapper.

Drawbacks

1. You don’t have access to the wmi types, constants and enumerations from the ide, because the code is interpreted in run-time.

2. Hard to debug syntax errors,because the compiler don’t know about the WMI types
so you can write something like this (which is wrong) and the code will be compiled anyway.

 colItems.Properties_('Prop').Qualifiers_;

the right version must be.

 colItems.Properties_.Item('Prop').Qualifiers_;

Check the code using Late Binding to access the WMI from delphi (Valid for versions 5 to XE)

program WmiDelphiWin32_LateBinding;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;//introduced in delphi 6, if you use a older version of delphi you just remove this

function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: Integer;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

//The Win32_BaseBoard class represents a base board (also known as a motherboard
//or system board).

procedure  GetWin32_BaseBoardInfo;
var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
    Writeln(Format('Caption                  %s',[VarStrNull(colItem.Caption)]));// String
    Writeln(Format('ConfigOptions            %s',[VarStrNull(colItem.ConfigOptions)]));// String
    Writeln(Format('CreationClassName        %s',[VarStrNull(colItem.CreationClassName)]));// String
    Writeln(Format('Depth                    %s',[VarStrNull(colItem.Depth)]));// Real32
    Writeln(Format('Description              %s',[VarStrNull(colItem.Description)]));// String
    Writeln(Format('Height                   %s',[VarStrNull(colItem.Height)]));// Real32
    Writeln(Format('HostingBoard             %s',[VarStrNull(colItem.HostingBoard)]));// Boolean
    Writeln(Format('HotSwappable             %s',[VarStrNull(colItem.HotSwappable)]));// Boolean
    Writeln(Format('InstallDate              %s',[VarStrNull(colItem.InstallDate)]));// Datetime
    Writeln(Format('Manufacturer             %s',[VarStrNull(colItem.Manufacturer)]));// String
    Writeln(Format('Model                    %s',[VarStrNull(colItem.Model)]));// String
    Writeln(Format('Name                     %s',[VarStrNull(colItem.Name)]));// String
    Writeln(Format('OtherIdentifyingInfo     %s',[VarStrNull(colItem.OtherIdentifyingInfo)]));// String
    Writeln(Format('PartNumber               %s',[VarStrNull(colItem.PartNumber)]));// String
    Writeln(Format('PoweredOn                %s',[VarStrNull(colItem.PoweredOn)]));// Boolean
    Writeln(Format('Product                  %s',[VarStrNull(colItem.Product)]));// String
    Writeln(Format('Removable                %s',[VarStrNull(colItem.Removable)]));// Boolean
    Writeln(Format('Replaceable              %s',[VarStrNull(colItem.Replaceable)]));// Boolean
    Writeln(Format('RequirementsDescription  %s',[VarStrNull(colItem.RequirementsDescription)]));// String
    Writeln(Format('RequiresDaughterBoard    %s',[VarStrNull(colItem.RequiresDaughterBoard)]));// Boolean
    Writeln(Format('SerialNumber             %s',[VarStrNull(colItem.SerialNumber)]));// String
    Writeln(Format('SKU                      %s',[VarStrNull(colItem.SKU)]));// String
    Writeln(Format('SlotLayout               %s',[VarStrNull(colItem.SlotLayout)]));// String
    Writeln(Format('SpecialRequirements      %s',[VarStrNull(colItem.SpecialRequirements)]));// Boolean
    Writeln(Format('Status                   %s',[VarStrNull(colItem.Status)]));// String
    Writeln(Format('Tag                      %s',[VarStrNull(colItem.Tag)]));// String
    Writeln(Format('Version                  %s',[VarStrNull(colItem.Version)]));// String
    Writeln(Format('Weight                   %s',[VarStrNull(colItem.Weight)]));// Real32
    Writeln(Format('Width                    %s',[VarStrNull(colItem.Width)]));// Real32
    Writeln('');
    colItem:=Unassigned;
  end;
end;

begin
 try
    CoInitialize(nil);
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
    CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

Using Delphi .Net (Valid for versions 2005, 2006, 2007)

1) before to use the .net wmi objects you add the System.Management reference to your project.

2) Now we need make the connection to the WMi service, to do this you must use the System.Management namespace and the ManagementObjectSearcher class wich have several constructor to facilitate the connection with the wmi, this class is very versatile and let establish the connection and make a WQL query in a single step.

Check the constructors availables to this class

Public method ManagementObjectSearcher() 	//Initializes a new instance of the ManagementObjectSearcher class. After some properties on this object are set, the object can be used to invoke a query for management information. This is the default constructor.

 

Public method ManagementObjectSearcher(ObjectQuery) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query for management information.

 

Public method ManagementObjectSearcher(String) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query for management information.

 

Public method ManagementObjectSearcher(ManagementScope, ObjectQuery) //Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query in the specified scope.

 

Public method ManagementObjectSearcher(String, String) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query in the specified scope.

 

Public method ManagementObjectSearcher(ManagementScope, ObjectQuery, EnumerationOptions) 	//Initializes a new instance of the

 

Public method ManagementObjectSearcher class //to be used to invoke the specified query in the specified scope, with the specified options.

 

Public method ManagementObjectSearcher(String, String, EnumerationOptions) 	//Initializes a new instance of the ManagementObjectSearcher class used to invoke the specified query, in the specified scope, and with the specified options.

so to establishing the connection and make the query to the WMI you must write a code like this

Searcher  :=ManagementObjectSearcher.Create('root\cimv2','SELECT * FROM Win32_BaseBoard');

3) retrieve the data using the ManagementObjectCollection object.

4) get a enumerator to iterate over the data returned using a ManagementObjectEnumerator

    Collection:=Searcher.Get();//Get the data
    iter:=Collection.GetEnumerator;//create  a enumerator
    while(iter.MoveNext()) do//iterate over the enumerator

5) and finally to access to the value of an particular property we must use a ManagementObject object in this way

      WmiObject:=ManagementObject(iter.Current); //get the current element
      SerialNumber:=WmiObject['SerialNumber'];

This is the full source code to access the wmi from Delphi .Net from a console application

program WmiDelphi.Net;
{$APPTYPE CONSOLE}
uses
  System.Management,
  SysUtils;

procedure  GetWin32_BaseBoardInfo;
var
Searcher   : ManagementObjectSearcher ;
Collection : ManagementObjectCollection;
iter       : ManagementObjectCollection.ManagementObjectEnumerator;
WmiObject  : ManagementObject;
begin
  try
    Searcher  :=ManagementObjectSearcher.Create('SELECT * FROM Win32_BaseBoard'); //make the WMi query
    Collection:=Searcher.Get();//Get the data
    iter:=Collection.GetEnumerator;//create  a enumerator
    while(iter.MoveNext()) do//iterate over the enumerator
    begin
      WmiObject:=ManagementObject(iter.Current); //get the current element
         Writeln(Format('Caption                 %s',[WmiObject['Caption']]));
         Writeln(Format('ConfigOptions           %s',[WmiObject['ConfigOptions']]));
         Writeln(Format('CreationClassName       %s',[WmiObject['CreationClassName']]));
         Writeln(Format('Depth                   %s',[WmiObject['Depth']]));
         Writeln(Format('Description             %s',[WmiObject['Description']]));
         Writeln(Format('Height                  %s',[WmiObject['Height']]));
         Writeln(Format('HostingBoard            %s',[WmiObject['HostingBoard']]));
         Writeln(Format('HotSwappable            %s',[WmiObject['HotSwappable']]));
         Writeln(Format('InstallDate             %s',[WmiObject['InstallDate']]));
         Writeln(Format('Manufacturer            %s',[WmiObject['Manufacturer']]));
         Writeln(Format('Model                   %s',[WmiObject['Model']]));
         Writeln(Format('Name                    %s',[WmiObject['Name']]));
         Writeln(Format('OtherIdentifyingInfo    %s',[WmiObject['OtherIdentifyingInfo']]));
         Writeln(Format('PartNumber              %s',[WmiObject['PartNumber']]));
         Writeln(Format('PoweredOn               %s',[WmiObject['PoweredOn']]));
         Writeln(Format('Product                 %s',[WmiObject['Product']]));
         Writeln(Format('Removable               %s',[WmiObject['Removable']]));
         Writeln(Format('Replaceable             %s',[WmiObject['Replaceable']]));
         Writeln(Format('RequirementsDescription %s',[WmiObject['RequirementsDescription']]));
         Writeln(Format('RequiresDaughterBoard   %s',[WmiObject['RequiresDaughterBoard']]));
         Writeln(Format('SerialNumber            %s',[WmiObject['SerialNumber']]));
         Writeln(Format('SKU                     %s',[WmiObject['SKU']]));
         Writeln(Format('SlotLayout              %s',[WmiObject['SlotLayout']]));
         Writeln(Format('SpecialRequirements     %s',[WmiObject['SpecialRequirements']]));
         Writeln(Format('Status                  %s',[WmiObject['Status']]));
         Writeln(Format('Tag                     %s',[WmiObject['Tag']]));
         Writeln(Format('Version                 %s',[WmiObject['Version']]));
         Writeln(Format('Weight                  %s',[WmiObject['Weight']]));
         Writeln(Format('Width                   %s',[WmiObject['Width']]));
    end;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end;

begin
  try
    GetWin32_BaseBoardInfo;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

Using Delphi Prism

By far using .Net and delphi prism must be the easy way to access the WMI, because you don’t need to implement enumerators or helper functions to avoid nulls. therefore exists many resources of the WMI and .Net in the MSDN site.

1) to access the WMI from .Net you add the System.Management namespace which give you full access to the WMI.

2) Now using the ManagementObjectSearcher class in a single step you can establish a connection to the wmi service and make a WQL query.

Searcher := new ManagementObjectSearcher('root\cimv2','select * from Win32_BaseBoard');

3) for iterate over the WMI data you can use the great features of dephi-prism, how create a local variable in a for loop

for WmiObject : ManagementObject  in searcher.Get() do //in this single line you are creating a WmiObject to get access to the properties,and searcher.Get() return a enumerator that iterates through the ManagementObjectCollection.

Finally the Source code of a Delphi Prism console application to access the WMI.

namespace WmiDelphiPrism;

interface
uses
System,
System.Management,
System.Text;

type
    ConsoleApp = class
    private
        class method GetWin32_BaseBoardInfo;
    public
        class method Main;
    end;

implementation

class method ConsoleApp.Main;
begin
 try
    GetWin32_BaseBoardInfo;
    Console.Read();
 except on E: Exception do
  Console.WriteLine(E.ToString()+' Trace '+E.StackTrace );
 end;
end;

class method ConsoleApp.GetWin32_BaseBoardInfo;
var
Searcher : ManagementObjectSearcher;
begin
     Searcher := new ManagementObjectSearcher('root\cimv2','select * from Win32_BaseBoard');
        for WmiObject : ManagementObject  in searcher.Get() do
        begin
             Console.WriteLine('{0,-35} {1,-40}','Caption',WmiObject['Caption']);
             Console.WriteLine('{0,-35} {1,-40}','ConfigOptions',WmiObject['ConfigOptions']);
             Console.WriteLine('{0,-35} {1,-40}','CreationClassName',WmiObject['CreationClassName']);
             Console.WriteLine('{0,-35} {1,-40}','Depth',WmiObject['Depth']);
             Console.WriteLine('{0,-35} {1,-40}','Description',WmiObject['Description']);
             Console.WriteLine('{0,-35} {1,-40}','Height',WmiObject['Height']);
             Console.WriteLine('{0,-35} {1,-40}','HostingBoard',WmiObject['HostingBoard']);
             Console.WriteLine('{0,-35} {1,-40}','HotSwappable',WmiObject['HotSwappable']);
             Console.WriteLine('{0,-35} {1,-40}','InstallDate',WmiObject['InstallDate']);
             Console.WriteLine('{0,-35} {1,-40}','Manufacturer',WmiObject['Manufacturer']);
             Console.WriteLine('{0,-35} {1,-40}','Model',WmiObject['Model']);
             Console.WriteLine('{0,-35} {1,-40}','Name',WmiObject['Name']);
             Console.WriteLine('{0,-35} {1,-40}','OtherIdentifyingInfo',WmiObject['OtherIdentifyingInfo']);
             Console.WriteLine('{0,-35} {1,-40}','PartNumber',WmiObject['PartNumber']);
             Console.WriteLine('{0,-35} {1,-40}','PoweredOn',WmiObject['PoweredOn']);
             Console.WriteLine('{0,-35} {1,-40}','Product',WmiObject['Product']);
             Console.WriteLine('{0,-35} {1,-40}','Removable',WmiObject['Removable']);
             Console.WriteLine('{0,-35} {1,-40}','Replaceable',WmiObject['Replaceable']);
             Console.WriteLine('{0,-35} {1,-40}','RequirementsDescription',WmiObject['RequirementsDescription']);
             Console.WriteLine('{0,-35} {1,-40}','RequiresDaughterBoard',WmiObject['RequiresDaughterBoard']);
             Console.WriteLine('{0,-35} {1,-40}','SerialNumber',WmiObject['SerialNumber']);
             Console.WriteLine('{0,-35} {1,-40}','SKU',WmiObject['SKU']);
             Console.WriteLine('{0,-35} {1,-40}','SlotLayout',WmiObject['SlotLayout']);
             Console.WriteLine('{0,-35} {1,-40}','SpecialRequirements',WmiObject['SpecialRequirements']);
             Console.WriteLine('{0,-35} {1,-40}','Status',WmiObject['Status']);
             Console.WriteLine('{0,-35} {1,-40}','Tag',WmiObject['Tag']);
             Console.WriteLine('{0,-35} {1,-40}','Version',WmiObject['Version']);
             Console.WriteLine('{0,-35} {1,-40}','Weight',WmiObject['Weight']);
             Console.WriteLine('{0,-35} {1,-40}','Width',WmiObject['Width']);
        end;
end;
end.

Using Lazarus

The code necessary to access the WMI from Free-pascal using lazarus is very similar to the used in the Delphi win32 Late-binding., so i will show the differences only because the general idea is the same.

1) you must add the Windows unit to the uses clause because this unit contan the PULONG type which is required in some functions.

2) you don’t need call CoInitialize and CoUninitialize functions because both are initializated by the comobj unit

3) the helper function GetWMIObject must be modified to fit with the new types of parameteres required by the MkParseDisplayName function.

which is declarated like this (see the _para3 param which is of PULONG (^cardinal) type)

 function MkParseDisplayName(_para1:IBindCtx; _para2:POLESTR; out _para3:PULONG; out _para4:IMoniker):HRESULT;stdcall; external  'ole32.dll' name 'MkParseDisplayName';

and the modified helper function now look like this.

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: PULONG;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

4) the function IEnumVARIANT.Next requires a Variant param

Function  Next(celt: ULONG; OUT rgVar: VARIANT;  pCeltFetched: pULONG=nil):HResult;StdCall;

so you must use it in this way

var
colItem : Variant;
//
//
//
while oEnum.Next(1, colItem, nil) = 0 do

5) finally if you wanna use the format function, you must aware which has some problems with variant values. so you must rewrite the code to show the info in this way.

    SDummy:=VarStrNull(colItem.Caption); //SDummy is a string
    Writeln(Format('Caption                  %s',[SDummy]));

Lazarus source code of console aplication to access the WMI

program WmiLazarus_LateBinding;

{$mode objfpc}

uses
   SysUtils,
   Variants,
   comobj,//required for the OleCheck and CoInitialize functions
   ActiveX,
   Windows;//required for the PULONG type

function VarArrayToStr(const vArray: variant): string;

    function _VarToStr(const V: variant): string;
    var
    Vt: integer;
    begin
    Vt := VarType(V);
        case Vt of
          varSmallint,
          varInteger  : Result := IntToStr(integer(V));
          varSingle,
          varDouble,
          varCurrency : Result := FloatToStr(Double(V));
          varDate     : Result := VarToStr(V);
          varOleStr   : Result := WideString(V);
          varBoolean  : Result := VarToStr(V);
          varVariant  : Result := VarToStr(Variant(V));
          varByte     : Result := char(byte(V));
          varString   : Result := String(V);
          varArray    : Result := VarArrayToStr(Variant(V));
        end;
    end;

var
i : integer;
begin
    Result := '[';
     if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);

    Result:=Result+']';
end;

function VarStrNull(const V:OleVariant):string; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=VarToStr(V);
  end;
end;

function GetWMIObject(const objectName: String): IDispatch; //create the Wmi instance
var
  chEaten: PULONG;
  BindCtx: IBindCtx;
  Moniker: IMoniker;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
  OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

//The Win32_BaseBoard class represents a base board (also known as a motherboard
//or system board).

procedure  GetWin32_BaseBoardInfo;
var
  objWMIService : OleVariant;
  colItems      : OleVariant;
  colItem       : Variant;
  oEnum         : IEnumvariant;
  SDummy        : string;
begin;
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
  while oEnum.Next(1, colItem, nil) = 0 do
  begin
    SDummy:=VarStrNull(colItem.Caption);
    Writeln(Format('Caption                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.ConfigOptions);
    Writeln(Format('ConfigOptions            %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.CreationClassName);
    Writeln(Format('CreationClassName        %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Depth);
    Writeln(Format('Depth                    %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.Description);
    Writeln(Format('Description              %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Height);
    Writeln(Format('Height                   %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.HostingBoard);
    Writeln(Format('HostingBoard             %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.HotSwappable);
    Writeln(Format('HotSwappable             %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.InstallDate);
    Writeln(Format('InstallDate              %s',[SDummy]));// Datetime
    SDummy:=VarStrNull(colItem.Manufacturer);
    Writeln(Format('Manufacturer             %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Model);
    Writeln(Format('Model                    %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Name);
    Writeln(Format('Name                     %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.OtherIdentifyingInfo);
    Writeln(Format('OtherIdentifyingInfo     %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.PartNumber);
    Writeln(Format('PartNumber               %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.PoweredOn);
    Writeln(Format('PoweredOn                %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Product);
    Writeln(Format('Product                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Removable);
    Writeln(Format('Removable                %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Replaceable);
    Writeln(Format('Replaceable              %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.RequirementsDescription);
    Writeln(Format('RequirementsDescription  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.RequiresDaughterBoard);
    Writeln(Format('RequiresDaughterBoard    %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.SerialNumber);
    Writeln(Format('SerialNumber             %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SKU);
    Writeln(Format('SKU                      %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SlotLayout);
    Writeln(Format('SlotLayout               %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.SpecialRequirements);
    Writeln(Format('SpecialRequirements      %s',[SDummy]));// Boolean
    SDummy:=VarStrNull(colItem.Status);
    Writeln(Format('Status                   %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Tag);
    Writeln(Format('Tag                      %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Version);
    Writeln(Format('Version                  %s',[SDummy]));// String
    SDummy:=VarStrNull(colItem.Weight);
    Writeln(Format('Weight                   %s',[SDummy]));// Real32
    SDummy:=VarStrNull(colItem.Width);
    Writeln(Format('Width                    %s',[SDummy]));// Real32
  end;

end;

begin
 try
   //CoInitialize(nil);   you don't need call this because is initializated by the comobj unit
    try
      GetWin32_BaseBoardInfo;
      Readln;
    finally
    //CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.

So now you don’t have excuses to don’t use the WMI for pascal code. And remember which you have the WMI Delphi Code creator to help you.;)


Leave a comment

ShineOn 1.0.1.0 released

ShineOn is a  library to assist porting Delphi/Win32 and Delphi for .NET projects to Delphi Prism by providing a subset of RTL and core VCL classes that can be used instead of replacing all RTL/VCL calls with native FCL alternatives.

ShineOn is an open source effort, More information can be found in the Prism Wiki, here.

You can download ShineOn from Here

Bye.


1 Comment

Access Point detection using Delphi Prism and ManagedWifi.

Detect  the AP availables using the Native Wifi API library is really simple. to do this we need to use GetNetworkBssList function which is a wrapper for the function WlanGetNetworkBssList.

This source code uses the Managed Wifi API, The library uses the Native Wifi API, available since Windows Vista and Windows XP SP2 (in a limited fashion, and only after applying a hotfix provided in KB article 918997). Older versions of Windows are not supported.
Before running the code you need to download the library from this location. Build the project “ManagedWifi.csproj” and then add the reference to the source code listed below.

namespace DelphiPrismAPWifidetection;
//Author : Rodrigo Ruz. 2009-10-15
//Shine Your crazy Diamond.

interface

uses
NativeWifi,//You must add the reference to the library ManagedWifi.dll
System.Text;

type
  ConsoleApp = class
  private
    class method GetStringForSSID(ssid : Wlan.Dot11Ssid ) : string;
    class method FrequencyToChannel(freq:Longint) : Integer;
  public
    class method Main;
  end;

implementation

class method ConsoleApp.FrequencyToChannel(freq:Longint) : Integer;
var
tmpval1,tmpval2 : Longint;
begin
  tmpval1 := (freq mod 2412000) div 1000;
  tmpval2 := tmpval1 div 5;
  Result  := tmpval2 + 1;
end;

class method ConsoleApp.GetStringForSSID(ssid : Wlan.Dot11Ssid ) : string;
begin
    Result:= Encoding.ASCII.GetString( ssid.SSID, 0, Int32( ssid.SSIDLength) );
end;

class method ConsoleApp.Main;
begin
var client : WlanClient  := new WlanClient();
    for each  wlanIface in client.Interfaces  do
    begin
    Console.WriteLine( "┌---------------------------------------------------------------¬");
    Console.WriteLine( "|{0,-15}  {1,-45} |",'Interface', wlanIface.InterfaceDescription);
    Console.WriteLine( "├---------------------------------------------------------------┤");
    Console.WriteLine( "|{0,-15}  {1,-45} |",'GUID', wlanIface.InterfaceGuid);
    Console.WriteLine( "|{0,-15}  {1,-45} |",'Name', wlanIface.InterfaceName);
    Console.WriteLine( "|{0,-15}  {1,-45} |",'State', wlanIface.InterfaceState.ToString());
    Console.WriteLine( "└---------------------------------------------------------------┘");

    var BssList  : Array of  Wlan.WlanBssEntry:= wlanIface.GetNetworkBssList();
    for each  BssItem in BssList  do
    begin
    // for more info goto http://msdn.microsoft.com/en-us/library/ms706735%28VS.85%29.aspx
    Console.WriteLine( "┌---------------------------------------------------------------¬");
    Console.WriteLine( "|Network SSID          {0,-40} |", GetStringForSSID(BssItem.dot11Ssid));
    Console.WriteLine( "├---------------------------------------------------------------┤");
    Console.WriteLine( "| Channel Freq. (Khz)  {0,-40} |", BssItem.chCenterFrequency.ToString());
    Console.WriteLine( "| Channel              {0,-40} |", FrequencyToChannel(BssItem.chCenterFrequency).ToString());
    Console.WriteLine( "| Rssi (dBm)           {0,-40} |", BssItem.rssi.ToString());
    Console.WriteLine( "| LinkQuality (%)      {0,-40} |", BssItem.linkQuality.ToString());
    Console.WriteLine( "| MAC                  {0,-40} |", BitConverter.ToString(BssItem.dot11Bssid));
    Console.WriteLine( "| dot11PhyType         {0,-40} |", BssItem.dot11BssPhyType.ToString());
    Console.WriteLine( "| network type         {0,-40} |", BssItem.dot11BssType.ToString());
    Console.WriteLine( "└---------------------------------------------------------------┘");
    end;
end;

Console.ReadLine();
end;

end.


Leave a comment

Using ParamStr in Delphi Prism

.Net does not include the ParamStr Function (The ParamStr function returns one of the parameters from the command line used to invoke the current program), but can be easily implemented, here is an example :

class method TMyClass.ParamStr(Index: Integer): String;
var
  MyAssembly: System.Reflection.Assembly;
  Params    : array of string;
begin
  if Index = 0 then
  begin
    MyAssembly:= System.Reflection.Assembly.GetEntryAssembly;
    if Assigned(MyAssembly) then
      Result := MyAssembly.Location
    else
      Result := System.Diagnostics.Process.GetCurrentProcess.MainModule.FileName;
  end
  else
  begin
    Params := System.Environment.GetCommandLineArgs;
    if Index > Length(Params) - 1 then
      Result := ''
    else
      Result := Params[Index];
  end;
end;


13 Comments

Detecting Wifi Networks Using Delphi Prism.

The next code uses the Managed Wifi API, The library uses the Native Wifi API, available since Windows Vista and Windows XP SP2 (in a limited fashion, and only after applying a hotfix provided in KB article 918997). Older versions of Windows are not supported.

Before to compile the code you need to download the library from this location. Build the project “ManagedWifi.csproj” and then add the reference to the source code listed below.
namespace DelphiPrismWifidetection;
//Author : Rodrigo Ruz. 2009-09-29
//Shine Your crazy Diamond.

interface

uses
NativeWifi,//You must add the reference to the library ManagedWifi.dll
System.Text;

type
  ConsoleApp = class
  private
    class method GetStringForSSID(ssid : Wlan.Dot11Ssid ) : string;
    class method GetStringFornumberOfBssids (numberOfBssids : Int32 ) : string;
  public
    class method Main;
  end;

implementation

class method ConsoleApp.GetStringForSSID(ssid : Wlan.Dot11Ssid ) : string;
begin
    Result:= Encoding.ASCII.GetString( ssid.SSID, 0, Int32( ssid.SSIDLength) );
end;

class method ConsoleApp.GetStringFornumberOfBssids (numberOfBssids : Int32 ) : string;
begin
  case numberOfBssids of
  1: Result:='infrastructure';
  2: Result:='independent';
  3: Result:='any';
  else
  Result:='Unknow';
  end; // case
end;

class method ConsoleApp.Main;
begin
            var client : WlanClient  := new WlanClient();
            for each  wlanIface in client.Interfaces  do
            begin
                var networks : Array of  Wlan.WlanAvailableNetwork  := wlanIface.GetAvailableNetworkList(NativeWifi.Wlan.WlanGetAvailableNetworkFlags.IncludeAllAdhocProfiles);
                for each  network in networks  do
                begin
                    // for more info goto http://msdn.microsoft.com/en-us/library/ms707403%28VS.85%29.aspx
                    Console.WriteLine( "┌---------------------------------------------------------------¬");
                    Console.WriteLine( "|Network Detected      {0,-40} |", GetStringForSSID(network.dot11Ssid));
                    Console.WriteLine( "├---------------------------------------------------------------┤");
                    Console.WriteLine( "| CipherAlgorithm      {0,-40} |", network.dot11DefaultCipherAlgorithm.ToString());
                    Console.WriteLine( "| DefaultAuthAlgorithm {0,-40} |", network.dot11DefaultAuthAlgorithm.ToString());
                    Console.WriteLine( "| dot11Ssid            {0,-40} |", network.dot11Ssid.ToString());
                    Console.WriteLine( "| networkConnectable   {0,-40} |", network.networkConnectable.ToString());
                    if not network.networkConnectable then
                    Console.WriteLine( "| NotConnectableReason {0,-40} |", network.wlanNotConnectableReason.ToString());
                    Console.WriteLine( "| morePhyTypes         {0,-40} |", network.morePhyTypes.ToString());
                    Console.WriteLine( "| (BSS) network type   {0,-40} |", GetStringFornumberOfBssids(network.numberOfBssids));
                    Console.WriteLine( "| profileName          {0,-40} |", network.profileName.ToString());
                    Console.WriteLine( "| securityEnabled      {0,-40} |", network.securityEnabled.ToString());
                    Console.WriteLine( "| wlanSignalQuality    {0,-40} |", network.wlanSignalQuality.ToString());
                    Console.WriteLine( "└---------------------------------------------------------------┘");
                end;

            end;
            Console.ReadLine();
end;

end.

and the result is

Update

Download the full source code from here

Bye.


Leave a comment

Enumerating All Network resources using Delphi Prism.

The next code uses the windows library mrp.dll (mpr.dll is a module containing functions used to handle communication between the Windows operating system and the installed network providers) and call these functions :

  • WNetOpenEnum (starts an enumeration of network resources or existing connections)
namespace DelphiPrismNetworkScan;
//Author : Rodrigo Ruz. 2009-09-28
//Shine Your crazy Diamond.

interface

uses
System,
System.Runtime.InteropServices;

type
        RESOURCE_SCOPE_NET= enum
        (
        RESOURCE_CONNECTED  = $00000001,
        RESOURCE_GLOBALNET  = $00000002,
        RESOURCE_REMEMBERED = $00000003,
        RESOURCE_RECENT     = $00000004,
        RESOURCE_CONTEXT    = $00000005
        );

        RESOURCE_TYPE_NET = enum
        (
        RESOURCETYPE_ANY      = $00000000,
        RESOURCETYPE_DISK     = $00000001,
        RESOURCETYPE_PRINT    = $00000002,
        RESOURCETYPE_RESERVED = $00000008
        );

        RESOURCE_USAGE_NET = enum
        (
        RESOURCEUSAGE_CONNECTABLE   =$00000001,
        RESOURCEUSAGE_CONTAINER     =$00000002,
        RESOURCEUSAGE_NOLOCALDEVICE =$00000004,
        RESOURCEUSAGE_SIBLING       =$00000008,
        RESOURCEUSAGE_ATTACHED      =$00000010,
        RESOURCEUSAGE_ALL           =(RESOURCEUSAGE_CONNECTABLE OR RESOURCEUSAGE_CONTAINER OR RESOURCEUSAGE_ATTACHED)
        );

        RESOURCE_DISPLAYTYPE_NET = enum
        (
          RESOURCEDISPLAYTYPE_GENERIC       = $00000000,
          RESOURCEDISPLAYTYPE_DOMAIN        = $00000001,
          RESOURCEDISPLAYTYPE_SERVER        = $00000002,
          RESOURCEDISPLAYTYPE_SHARE         = $00000003,
          RESOURCEDISPLAYTYPE_FILE          = $00000004,
          RESOURCEDISPLAYTYPE_GROUP         = $00000005,
          RESOURCEDISPLAYTYPE_NETWORK       = $00000006,
          RESOURCEDISPLAYTYPE_ROOT          = $00000007,
          RESOURCEDISPLAYTYPE_SHAREADMIN    = $00000008,
          RESOURCEDISPLAYTYPE_DIRECTORY     = $00000009,
          RESOURCEDISPLAYTYPE_TREE          = $0000000A,
          RESOURCEDISPLAYTYPE_NDSCONTAINER  = $0000000B
        );

  NETRESOURCE  = public record
    var     dwScope         : RESOURCE_SCOPE_NET;
    var     dwType          : RESOURCE_TYPE_NET;
    var     dwDisplayType   : RESOURCE_DISPLAYTYPE_NET;
    var     dwUsage         : RESOURCE_USAGE_NET;
    var     [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]  lpLocalName : String;
    var     [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]  lpRemoteName: String;
    var     [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]  lpComment   : String;
    var     [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]  lpProvider  : String;
  end;

  ConsoleApp = class
  public
        [DllImport("mpr.dll", CharSet:=CharSet.Auto)]
        class method WNetEnumResource(hEnum: IntPtr; var lpcCount: Integer; lpBuffer: IntPtr; var lpBufferSize: Integer): Integer;external;
        [DllImport("mpr.dll", CharSet:=CharSet.Auto)]
        class method WNetOpenEnum( dwScope: RESOURCE_SCOPE_NET; dwType: RESOURCE_TYPE_NET; dwUsage: RESOURCE_USAGE_NET; [MarshalAs(UnmanagedType.AsAny)]  [&In()]  lpNetResource: Object; out lphEnum: IntPtr): Integer;external;
        [DllImport("mpr.dll", CharSet:=CharSet.Auto)]
        class method WNetCloseEnum(hEnum: IntPtr): Integer;external;
        class method InitScan(Dummy: Object);
        class method Main;
  end;

implementation

class method ConsoleApp.InitScan(Dummy: Object);
  var
  iRet     : Integer;
  ptrHandle: IntPtr  := new IntPtr();
  entries  : Integer;
  buffer   : Integer := 16384;
  nr       : NETRESOURCE;
  ptrBuffer: IntPtr:=Marshal.AllocHGlobal(buffer);
begin
try
    //Init the enumeration , you can change the paremeters to filter.
    iRet := WNetOpenEnum(RESOURCE_SCOPE_NET.RESOURCE_GLOBALNET, RESOURCE_TYPE_NET.RESOURCETYPE_ANY, RESOURCE_USAGE_NET.RESOURCEUSAGE_ALL, Dummy, out ptrHandle);
    if iRet <> 0 then
    begin
      exit;
    end;

        while true do //infinite loop
        Begin
                entries := -1;
                buffer  := 16384;
                iRet    := WNetEnumResource(ptrHandle, var entries, ptrBuffer, var buffer); //Load the next resource

                if ((iRet <> 0)) OR ((entries < 1)) then //if fails or non exist any entries then exit
                begin
                Break;
                end;

            var ptr: Int32 := ptrBuffer.ToInt32();
            var i  : Int32 :=0;
            while i< entries do
            Begin
                nr := NETRESOURCE(Marshal.PtrToStructure(new IntPtr(ptr), typeOf(NETRESOURCE)));
                if RESOURCE_USAGE_NET.RESOURCEUSAGE_CONTAINER = (nr.dwUsage and RESOURCE_USAGE_NET.RESOURCEUSAGE_CONTAINER) then //if is a contanier the function scan the resource again.
                begin
                  InitScan(nr); //recursive call to the function
                end;

                ptr :=ptr+ Marshal.SizeOf( nr );
                Console.WriteLine("{0}", nr.dwDisplayType.ToString());
                Console.WriteLine(" Type       ={0}", nr.dwType.ToString());
                Console.WriteLine(" Usage      ={0}", nr.dwUsage.ToString());
                Console.WriteLine(" Scope      ={0}", nr.dwScope.ToString());
                Console.WriteLine(" LocalName  ={0}", nr.lpLocalName);
                Console.WriteLine(" RemoteName ={0}", nr.lpRemoteName);
                Console.WriteLine(" Description={0}", nr.lpComment);
                Console.WriteLine(" Provider   ={0}", nr.lpProvider);
                Console.WriteLine();

                inc(i);
            End;

        End;

  except
    on e: Exception do
    begin
      Console.WriteLine('Error ** ' + e.Message + ' ** Trace ' + e.StackTrace)
    end;
end;
end;

class method ConsoleApp.Main;
begin
      Console.WriteLine('Scannig Network....Wait a moment , be patient please');
      Console.WriteLine();
      InitScan(nil);
      Console.WriteLine('Scan Network Finish');
      Console.Read();
end;

end.

When you run this code you’ll get something like this

Bye.