////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// The WMI is Windows Management Instrumentation.                             //
// The WQL is Windows Management Instrumentation Query Language.              //
// This unit implement WMI Collection - functions to work with WMI.           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20250209 - Created by A.K.                                                 //
////////////////////////////////////////////////////////////////////////////////

unit _crw_wmic; //  Unit for WMI.

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math, variants,
 {$IFDEF WINDOWS}
 ComObj, ActiveX, JwaWbemCli,
 {$ENDIF ~WINDOWS}
 _crw_alloc, _crw_ef;

 ///////////////////////////////////////////////////////////////////////////////
 // Get list of running processes by using WMI query.
 // If (aPid<>0) or (aPPid<>0) or (aname<>'') then use conditional search
 // SELECT * FROM Win32_Process WHERE ProcessId=aPid AND NAME="aName" ...
 // Output format:  PID, PPID, PRIORITY, TASKNAME, THREADS, CMDLINE
 ///////////////////////////////////////////////////////////////////////////////
function WMI_GetListOfProcesses(aPid,aPPid:TPid; aName:LongString; aFlags:Integer=-1):LongString;

function glops_wmicdef:Integer; inline;

implementation

uses _crw_proc, _crw_procps;

function glops_wmicdef:Integer;
begin
  Result:=glops_threads or glops_cmdline;
end;

{$IFDEF WINDOWS}
function WMI_GetListOfProcesses(aPid,aPPid:TPid; aName:LongString; aFlags:Integer=-1):LongString;
const WbemFlagForwardOnly=$00000020; // ForwardOnly enumerator working faster
const WbemComputer='localhost'; WbemUser=''; WbemPassword=''; // WMI Account
var FSWbemLocator,FWMIService,FWbemObjectSet,FWbemObject:OLEVariant;
var oEnum:IEnumvariant; iv:LongWord; Line,WQLQuery,Cond:LongString;
var Pid,PPid:TPid; Priority,Threads:LongInt;
var CmdLine,TaskName:String;
var Lines:TStringList;
 procedure Cleanup;
 begin // Clear/Free all except Lines
  FSWbemLocator:=Unassigned; FWMIService:=Unassigned;
  FWbemObjectSet:=Unassigned; FWbemObject:=Unassigned;
  CmdLine:=''; TaskName:=''; Line:=''; WQLQuery:='';
  Cond:=''; oEnum:=nil; iv:=0; Pid:=0; PPid:=0;
  Priority:=0; Threads:=0;
 end;
 procedure AndCond(var Cond:LongString; Expr:LongString);
 begin
  if (Cond='') then Cond:=Expr else Cond:=Cond+' AND '+Expr;
 end;
 function VarToStr(v:Variant):String;
 begin
  if VarIsStr(v) then Result:=v else Result:='';
 end;
 function FormatPs(Pid,PPid:TPid; Priority:LongInt; const TaskName:LongString; Threads:LongInt; const CmdLine:LongString):LongString;
 var Flags:Integer;
 begin
  Flags:=aFlags; if (CmdLine='') then LiftFlags(Flags,glops_CmdLine,False);
  Result:=glops_format(Pid,PPid,Priority,TaskName,pps_Running,Threads,CmdLine,Flags);
 end;
begin
 Result:='';
 try
  if (aFlags=-1) then aFlags:=glops_wmicdef;
  if Succeeded(CoInitialize(nil)) then
  try
   Lines:=TStringList.Create;
   try
    Cleanup;
    try
     aName:=Trim(aName);
     FWbemObject:=Unassigned; Cond:='';
     WQLQuery:='SELECT * FROM Win32_Process';
     if (aPid<>0) or (aPPid<>0) or (aName<>'') then begin
      if (aPid<>0) then AndCond(Cond,'ProcessId='+IntToStr(aPid));
      if (aPPid<>0) then AndCond(Cond,'ParentProcessId='+IntToStr(aPPid));
      if (aName<>'') then AndCond(Cond,'Name='+AnsiQuotedStr(aName,'"'));
      if (Cond<>'') then WQLQuery:=WQLQuery+' WHERE '+Cond;
     end;
     FSWbemLocator:=CreateOleObject('WbemScripting.SWbemLocator');
     FWMIService:=FSWbemLocator.ConnectServer(WbemComputer,'root\CIMV2',WbemUser,WbemPassword);
     FWbemObjectSet:=FWMIService.ExecQuery(WQLQuery,'WQL',WbemFlagForwardOnly);
     oEnum:=(IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant);
     if Assigned(oEnum) then begin
      while (oEnum.Next(1,FWbemObject,iv)=0) do
      try
       Line:='';
       Pid:=FWbemObject.ProcessId;
       Priority:=FWbemObject.Priority;
       Threads:=FWbemObject.ThreadCount;
       PPid:=FWbemObject.ParentProcessId;
       TaskName:=VarToStr(FWbemObject.Name);
       //TaskName:=VarToStr(FWbemObject.Caption);
       CmdLine:=VarToStr(FWbemObject.CommandLine);
       if (Pid>0) and (TaskName<>'') then begin
        // Skip 'System Idle Process' with Pid=0 and empty names
        Line:=FormatPs(Pid,PPid,Priority,TaskName,Threads,CmdLine);
       end;
       if (Line<>'') then Lines.Add(Line);
      finally
       FWbemObject:=Unassigned;
      end;
     end;
    finally
     Cleanup;
    end;
    Result:=Lines.Text;
   finally
    Lines.Free;
   end;
  finally
   CoUnInitialize();
  end;
 except
  on E:Exception do BugReport(E,nil,'WMI_GetListOfProcesses');
 end;
end;
{$ELSE ~WINDOWS}
function WMI_GetListOfProcesses(aPid,aPPid:TPid; aName:LongString; aFlags:Integer=-1):LongString;
begin
 Result:='';
end;
{$ENDIF ~WINDOWS}

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_wmic;
begin
end;

procedure Free_crw_wmic;
begin
end;

initialization

 Init_crw_wmic;

finalization

 Free_crw_wmic;

end.

//////////////
// END OF FILE
//////////////

