[WMI] Queries...

I use this WMI query to get all running processes: select * from Win32_Process
But I would like to have something like: select * from Win32_Process order by Name

Of course, with WMI, the latter option just doesn't work, so I'm wondering if there's another way to tell the system to order the info in some particular order or do I have to sort it myself?

And no, I'm not using any WMI component set, just the wbemdisp.tlb type library. (Simply because I'm calling WMI from a simple console application. I don't want the additional VCL overhead included in my project.)

I assume the answer is: sort it yourself. But if someone can provide me an alternative solution purely based on the WMI type library then he (or she) will earn the points.
LVL 17
Wim ten BrinkSelf-employed developerAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

DragonSlayerCommented:
You can use an ActiveX Data Objects (ADO) disconnected Recordset to sort the data... no time to code it now, will find time later...
0
Wim ten BrinkSelf-employed developerAuthor Commented:
Maybe. But then I need to import the ADO typelibrary in my application also. Makes it a bit more complicated but as long as I can avoid having to add VCL components, I'm happy. :-)
I do wonder if a WMI objectset is in any way compatible with an ADO recordset, though. If not, it's not an option because it just means I have to sort it myself again...
0
DragonSlayerCommented:
well, (as far as I know) it is not compatible, if you were thinking of direct usage of ObjectSet as a RecordSet...

so, what I was actually suggesting was that you create an ADO disconnected R/set, loop thru your WMI and insert into your ADO... and from there you can use the SORT commands, etc... and you can do other more 'advanced' query with it.

Sorry if that made you happy for a while, only to make you disappointed again ;-)
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

Wim ten BrinkSelf-employed developerAuthor Commented:
Am disappointed now. :-(
Actually, sorting the data myself isn't a real problem. If need be, I just dump the process names in a sorted stringlist, with a reference to the WMI objects linked to it. Thing is, sorting the data is not the problem. I just don't want to sort it myself. Too much trouble for something that should be quite easy... But apparantly it's not supported.
0
DragonSlayerCommented:
Hmm... another idea is to export the WMI results to XML, then read it in using RecordSet... however, I only know of the IIS WMI objects that has the Export method... unsure about the others...
0
DragonSlayerCommented:
> Actually, sorting the data myself isn't a real problem

Well, if you really do a loop and dump the whole chunk into a recordset instead, you can do sort by calling the ADO's Sort method, instead of implementing your own...
0
DragonSlayerCommented:
Gosh... can't really help you more till tomorrow ... my harddisk crashed earlier on, and I'm replying this from a cyber cafe, lolx... wish me luck in fixing the hard disk, and I'll be back to you soon ;-)
0
Wim ten BrinkSelf-employed developerAuthor Commented:
Good luck with your harddisk. I hope he gets better soon. ;-)
If not, let him rust in peace...
0
Russell LibbySoftware Engineer, Advisory Commented:
Workshop_Alex,

The answer to the original question is no. Perhaps MS will add it (to all of wmi) in the near future, as they have already done with the SMS wmi query processor. For SMS 2003, they call it "extended" WQL, and it supports the following additions to the WQL language:

DISTINCT
COUNT
JOIN
WHERE
SUBSTRING
ORDER BY
UPPER, LOWER, and DATEPART functions

...

Now....
 I know that you said above that sorting this manually is easy enough to do (true), But, what I am providing below is a means to perform the sorting for you in a dynamic fashion. By passing in the name of the property you wish to sort on, the utility function will create an interface object that extracts the interfaces from the object set and then orders them by the property value. The sorting is fairly intelligent, and is more than a simple string sort. For example, if the values are integer, then 2 will show before 10, etc.
The sorted set can then be walked using index and will return the ISWbemObject's in sorted order. While this is not "perfect" (wmi doing it for us), it does provide a generic and dynamic means of sorting objects in a set based on whatever property you wish to use.

Example of usage:

var  wmiServices:   ISWbemServices;
     wmiSorted:     ISWbemSortedSet;
     wmiProcess:    ISWbemObject;
     dwIndex:       Integer;
begin

  // Get the services object
  if (WmiGetObject('winmgmts:root\cimv2', ISWbemServices, wmiServices) = S_OK) then
  begin
     // Execute a query and sort the result based on a specific property
     wmiSorted:=WmiExecSorted(wmiServices.ExecQuery('select * from Win32_Process', 'WQL', wbemFlagReturnWhenComplete, nil), 'ProcessID');
     try
        // Do whatever with sorted list of objects
        for dwIndex:=0 to wmiSorted.Count-1 do
        begin
           wmiProcess:=wmiSorted.Item[dwIndex];
           try
              ShowMessage(String(wmiProcess.Properties_.Item('Name', 0).Get_Value));
           finally
              // Release interface
              wmiProcess:=nil;
           end;
        end;
     finally
        // Release interface
        wmiSorted:=nil;
     end;
     // Release interface
     wmiServices:=nil;
  end;

end;


The source code that supports the above example is listed below.

Hope this helps,
Russell


------------------------------------------

unit WmiUtil;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit           :  WmiUtil
//   Date           :  11.05.2004
//   Author         :  rllibby
//
//   Description    :  Set of utility routines for WMI handling
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows,
  SysUtils,
  Classes,
  ComObj,
  ActiveX,
  WbemScripting_Tlb;

////////////////////////////////////////////////////////////////////////////////
//   Sorting type record declaration
////////////////////////////////////////////////////////////////////////////////
type
  PObjSortEntry     =  ^TObjSortEntry;
  TObjSortEntry     =  packed record
     Obj:           IUnknown;
     Value:         OleVariant;
  end;

////////////////////////////////////////////////////////////////////////////////
//   ISWbemSortedSet interface declaration
////////////////////////////////////////////////////////////////////////////////
type
  ISWbemSortedSet   =  interface
     function       GetCount: Integer;
     function       GetItem(Index: Integer): ISWbemObject;
     property       Count: Integer read GetCount;
     property       Item[Index: Integer]: ISWbemObject read GetItem;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Utility routines
////////////////////////////////////////////////////////////////////////////////
function   WmiGetObject(Value: String; iidResult: TGUID; out Obj): HResult;
function   WmiExecSorted(ObjectSet: ISWbemObjectSet; OrderBy: String): ISWbemSortedSet;

implementation

function ObjEntrySortCompare(Item1, Item2: Pointer): Integer;
var  lpItem1:       PObjSortEntry;
     lpItem2:       PObjSortEntry;
begin

  // Get items
  lpItem1:=Item1;
  lpItem2:=Item2;

  // Check item1 for unassigned and null
  if (VarType(lpItem1^.Value) in [varEmpty, varNull]) then
  begin
     // Check second item
     if (VarType(lpItem2^.Value) in [varEmpty, varNull]) then
        result:=0
     else
        result:=-1;
  end
  // Check item2 for unassigned and null
  else if (VarType(lpItem2^.Value) in [varEmpty, varNull]) then
  begin
     // Check first item
     if (VarType(lpItem1^.Value) in [varEmpty, varNull]) then
        result:=0
     else
        result:=1;
  end
  else
  begin
     // Compare variants
     if (lpItem1^.Value < lpItem2^.Value) then
        result:=-1
     else if (lpItem1^.Value > lpItem2^.Value) then
        result:=1
     else
        result:=0;
  end;

end;

////////////////////////////////////////////////////////////////////////////////
//   Sorted class definition (not creatable externally)
////////////////////////////////////////////////////////////////////////////////
type
  TWbemSortedSet    =  class(TObject, IUnknown, ISWbemSortedSet)
  private
     // Private declarations
     FRefCount:     Integer;
     FSortList:     TInterfaceList;
     procedure      AddSorted(ObjectSet: ISWbemObjectSet; OrderBy: String);
  protected
     // Protected declarations
     function       QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
     function       _AddRef: Integer; stdcall;
     function       _Release: Integer; stdcall;
     function       GetCount: Integer;
     function       GetItem(Index: Integer): ISWbemObject;
  public
     // Public declarations
     constructor    Create(ObjectSet: ISWbemObjectSet; OrderBy: String);
     destructor     Destroy; override;
     property       Count: Integer read GetCount;
     property       Item[Index: Integer]: ISWbemObject read GetItem;
  end;

function TWbemSortedSet.QueryInterface(const IID: TGUID; out Obj): HResult;
begin

  // Get the requested interface
  if GetInterface(IID, Obj) then
     // Success
     result:=S_OK
  else
     // No such interface
     result:=E_NOINTERFACE;

end;
function TWbemSortedSet._AddRef: Integer;
begin

  // Increment the ref count
  result:=InterlockedIncrement(FRefCount);

end;

function TWbemSortedSet._Release: Integer;
begin

  // Decrement the ref count
  result:=InterlockedDecrement(FRefCount);

  // Destroy the component if the count reaches zero
  if (result = 0) then Destroy;

end;

procedure TWbemSortedSet.AddSorted(ObjectSet: ISWbemObjectSet; OrderBy: String);
var  pevEnum:       IEnumVariant;
     lpEntry:       PObjSortEntry;
     ovItem:        OleVariant;
     objSort:       TList;
     dwFetch:       LongWord;
begin

  // Create sorting list
  objSort:=TList.Create;

  // Resource protection
  try
     // Get enumerator from the set
     pevEnum:=ObjectSet._NewEnum as IEnumVariant;
     // Resource protection
     try
        // Enumerate the interfaces and add to the list
        while (pevEnum.Next(1, ovItem, dwFetch) = S_OK) do
        begin
           // Resource protection
           try
              // Create record for sorting
              lpEntry:=AllocMem(SizeOf(TObjSortEntry));
              // Save interface
              lpEntry^.Obj:=ovItem;
              // Get the value
              if (OrderBy = EmptyStr) then
                 // Use null value
                 lpEntry^.Value:=Null
              else
                 // Get the property value
                 lpEntry^.Value:=ISWbemObject(IDispatch(ovItem)).Properties_.Item(OrderBy, 0).Get_Value;
              // Add entry to list
              objSort.Add(lpEntry);
           finally
              // Clear variant
              ovItem:=Unassigned;
           end;
        end;
        // Sort the list
        objSort.Sort(ObjEntrySortCompare);
        // Now add to the interface list
        for dwFetch:=0 to Pred(objSort.Count) do
        begin
           // Get entry
           lpEntry:=objSort[dwFetch];
           // Add to list
           FSortList.Add(lpEntry^.Obj);
        end;
     finally
        // Release interface
        pevEnum:=nil;
     end;
  finally
     // Clear all items
     for dwFetch:=Pred(objSort.Count) downto 0 do
     begin
        // Get entry
        lpEntry:=objSort[dwFetch];
        // Release interface and clear variant
        lpEntry^.Obj:=nil;
        lpEntry^.Value:=Unassigned;
        // Free entry memory
        FreeMem(lpEntry);
     end;
     // Free the sorting list
     objSort.Free;
  end;

end;

function TWbemSortedSet.GetCount: Integer;
begin

  // Return the list count
  result:=FSortList.Count;

end;

function TWbemSortedSet.GetItem(Index: Integer): ISWbemObject;
begin

  // Return the item at the desired index
  result:=FSortList[Index] as ISWbemObject;

end;

constructor TWbemSortedSet.Create(ObjectSet: ISWbemObjectSet; OrderBy: String);
begin

  // Perform inherited
  inherited Create;

  // Set starting ref count
  FRefCount:=0;

  // Create list
  FSortList:=TInterfaceList.Create;

  // Add to the interface list in sorted order
  AddSorted(ObjectSet, OrderBy);

end;

destructor TWbemSortedSet.Destroy;
begin

  // Free the interface list
  FSortList.Free;

  // Perform inherited
  inherited Destroy;

end;

function WmiExecSorted(ObjectSet: ISWbemObjectSet; OrderBy: String): ISWbemSortedSet;
begin

  // Create sorted set interface
  result:=TWbemSortedSet.Create(ObjectSet, OrderBy);

end;

function WmiGetObject(Value: String; iidResult: TGUID; out Obj): HResult;
var  pmk:        IMoniker;
     pbc:        IBindCtx;
     cbEaten:    LongInt;
begin

  // Try to access the object using a moniker
  result:=CreateBindCtx(0, pbc);

  // Check result code
  if (result = S_OK) then
  begin
     // Parse the moniker display name
     result:=MkParseDisplayName(pbc, StringToOleStr(Value), cbEaten, pmk);
     // Check result code
     if (result = S_OK) then
     begin
        // Attempt to bind the moniker
        result:=BindMoniker(pmk, 0, iidResult, Obj);
        // Release the interface
        pmk:=nil;
     end;
     // Release interface
     pbc:=nil;
  end;

end;

end.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Wim ten BrinkSelf-employed developerAuthor Commented:
Yep, One nice way to sort the objectset, rllibby. Real nice piece of code. But I had a simpler solution in mind already where I just use a dynamic array of SWbemObject on which I could do a simple QuickSort. I just need to enumerate the objects in the objectset and add them to the array. I've tested it and it works great. And it's a lot less complex too. ;-)

Still, if no better options are given, you'll be given the point(A) for this nice piece of code. Still, one thing... Your WmiGetObject() function is nice, but I just use this code:

const
  sQuery = 'select * from Win32_Process';
var
  Enum: IEnumVariant;
  I: Integer;
  Item: SWbemObject;
  List: array of SWbemObject;
  ObjectSet: ISWbemObjectSet;
  Services: ISWbemServices;
begin
  Services := CoSWbemLocator.Create.ConnectServer( '', 'root\cimv2', '', '', '', '', 0, nil );
  ObjectSet := Services.ExecQuery( sQuery, 'WQL', wbemFlagBidirectional, nil );
  Enum := ObjectSet._NewEnum as IEnumVariant;
  SetLength( List, ObjectSet.Count );
  I := Low( List );
  while NextItem( Enum, SWBemObject, Item ) do begin
    if ( I > High( List ) ) then SetLength( List, I );
    List[ I ] := Item;
    Inc( I );
  end;
  // Here, code for a QuickSort or other sorting routine is required...

This is just a lot easier in my opinion... ;-)
Still have to create a better sorting routine, though. But that's not a real problem. I have all the objects as properly typed values already, so I don't have to work on that. It's just that I disliked having to add this additional code.

And to enumerate objects in an objectset, I use:

function NextItem( var Enum: IEnumVARIANT; const riid: TGUID; out ppObject ): Boolean;
var
  OleProperty: OleVariant;
  NumProp: LongWord;
begin
  try
    Result := Succeeded( Enum.Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( riid, ppObject ) );
  except
    Result := False;
    IUnknown( ppObject ) := nil;
  end;
end;

Above little function works great for returning an object of the preferred class and is often the only location in my code where I use an OleVariant...

Apparantly Extended WMI has a sort option with the 'ORDER BY' keywords. Too bad I also have to support the old-style WMI...
0
Wim ten BrinkSelf-employed developerAuthor Commented:
It wasn't really the answer I was looking for but hey, it did gave me some new ideas. And the information about extended WMI was interesting too. Still, the solution I have now seems to work better.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.