unit uCICExecuteProgram;

// ALE 20210228 inspired by
//  Gajic, Zarko. "Execute and Run Applications and Files From Delphi Code."
//  ThoughtCo, Feb. 16, 2021,
//  https://thoughtco.com/execute-and-run-applications-1058462

interface

uses
  System.SysUtils, System.Classes, System.Generics.Collections, Winapi.ShellAPI,
  Winapi.Windows, VCL.ExtCtrls;

type
  TCICExecuteProgramOnTimer = procedure() of object;
  TCICExecuteProgramOnAppLoaded = procedure() of object;
  TCICExecuteProgram = class
  protected
    FTimer: TTimer;
    FTimerEvent: TCICExecuteProgramOnTimer;
    FAppLoadedEvent: TCICExecuteProgramOnAppLoaded;
    FSEInfo: TShellExecuteInfo;
    FFileToExecute: String;
    FStartInDir: String;
    FParameters: String;
    FExitCode: Cardinal;
    //function IsAppLoaded(): Boolean;
  private
    FIsAppLoaded: Boolean;
  public
    constructor Create(); overload;
    destructor Destroy(); overload;
    function RunIt: Boolean;
    function IsStillRunning: Boolean;
  published
    property AppLoaded: Boolean read FIsAppLoaded;
    property ShellExecuteInfo: TShellExecuteInfo read FSEInfo;
    property FileToExecute: String read FFileToExecute write FFileToExecute;
    property StartInDir: String read FStartInDir write FStartInDir;
    property Parameters: String read FParameters write FParameters;
    property OnTimer: TCICExecuteProgramOnTimer read FTimerEvent write FTimerEvent;
    property OnAppLoaded: TCICExecuteProgramOnAppLoaded read FAppLoadedEvent write FAppLoadedEvent;
  end;
implementation

{ TCICExecuteProgram }

constructor TCICExecuteProgram.Create;
begin
  inherited;

  FillChar(FSEinfo, SizeOf(FSEInfo), 0);
  FSEInfo.cbSize := SizeOf(TShellExecuteInfo);

  FTimer := TTimer.Create(nil);
  FTimer.Enabled := False;
  //FTimer.OnTimer := FTimerEvent;
end;

destructor TCICExecuteProgram.Destroy;
begin
  FTimer.Enabled := False;
  FTimer.Free;

  inherited;
end;

function TCICExecuteProgram.IsStillRunning: Boolean;
begin
  GetExitCodeProcess(FSEInfo.hProcess, FExitCode);
  if (ExitCode <> STILL_ACTIVE) then
    Result := True
  else
    Result := False;
end;

function TCICExecuteProgram.RunIt: Boolean;
begin
  FSEInfo.fMask := SEE_MASK_NOCLOSEPROCESS OR SEE_MASK_FLAG_NO_UI; // ALE 20210228 allows us to capture PID
  // ALE 20210228 optional parent window FSEInfo.Wnd := ;
  FSEInfo.lpVerb := 'open';
  FSEInfo.lpFile := PWideChar(FFileToExecute);
  FSEInfo.lpDirectory := PWideChar(FStartInDir);
  FSEInfo.lpParameters := PWideChar(FParameters);
  FSEInfo.nShow := SW_SHOWNORMAL;

  if ShellExecuteEx(@FSEInfo) then
  begin
    Result := True;
  end
  else
  begin
    Result := False;
  end;
end;

end.
