Thursday, 1 December 2016

Create a Job object And terminate Child Process

How to gracefully handle when main.exe terminated, child.exe terminated also?
You need to use jobs. Main executable should create a job object, then you'll need to set JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag to your job object.
uses
  JobsApi;
//...
var
  jLimit: TJobObjectExtendedLimitInformation;

  hJob := CreateJobObject(nil, PChar('JobName');
  if hJob <> 0 then
  begin
    jLimit.BasicLimitInformation.LimitFlags := JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
      SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, @jLimit,
        SizeOf(TJobObjectExtendedLimitInformation));
  end; 
Then you need to execute another process with CreateProcess function where dwCreationFlags must be set to CREATE_BREAKAWAY_FROM_JOB. If this function succeeds call AssignProcessToJobObject.
function ExecuteProcess(const EXE : String; const AParams: string = ''; AJob: Boolean = True): THandle;
var
  SI : TStartupInfo;
  PI : TProcessInformation;
  AFlag: Cardinal;
begin
  Result := INVALID_HANDLE_VALUE;
  FillChar(SI,SizeOf(SI),0);
  SI.cb := SizeOf(SI);

  if AJob then
    AFlag := CREATE_BREAKAWAY_FROM_JOB
  else
    AFlag := 0;


  if CreateProcess(
     nil,
     PChar(EXE + ' ' + AParams),
     nil,
     nil,
     False,
     AFlag,
     nil,
     nil,
     SI,
     PI
     ) then
  begin
   { close thread handle }
    CloseHandle(PI.hThread);
    Result := PI.hProcess;
  end;
end;
//...
  hApp := ExecuteProcess('PathToExecutable');

  if hApp <> INVALID_HANDLE_VALUE then
  begin
     AssignProcessToJobObject(hJob, hApp);
  end;
When all of this done all the child processes will be automatically terminated even if the main executable has been killed. You can get the JobsApi unit here.

If you nee add some changes to be able user to set show window flags for child processes like SW_SHOW/SW_HIDE.

function ExecuteProcess(const EXE : String; const AParams: string = '';
  const nCmdShow: Integer = SW_SHOW; AJob: Boolean = True): THandle;
var
  SI : TStartupInfo;
  PI : TProcessInformation;
  AFlag: Cardinal;
begin
  Result := INVALID_HANDLE_VALUE;
  FillChar(SI,SizeOf(SI),0);
  SI.cb := SizeOf(SI);
  SI.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  SI.wShowWindow := nCmdShow;

  if AJob then
    AFlag := CREATE_BREAKAWAY_FROM_JOB
  else
    AFlag := 0;
........
Demo Project can be downloaded here.
Or For JobsApi Take a look here:
unit JobsApi;

interface

uses
  Windows;


type
  TJobObjectInfoClass   =   Cardinal;

  PJobObjectAssociateCompletionPort   =   ^TJobObjectAssociateCompletionPort;
  TJobObjectAssociateCompletionPort   =   Record
      CompletionKey     :   Pointer;
      CompletionPort   :   THandle;
  End;

  PJobObjectBasicLimitInformation   =   ^TJobObjectBasicLimitInformation;
  TJobObjectBasicLimitInformation   =  packed Record
      PerProcessUserTimeLimit   :   TLargeInteger;
      PerJobUserTimeLimit           :   TLargeInteger;
      LimitFlags                             :   DWORD;
      MinimumWorkingSetSize       :   DWORD;
      MaximumWorkingSetSize       :   DWORD;
      ActiveProcessLimit             :   DWORD;
      Affinity                                 :   DWORD;
      PriorityClass                       :   DWORD;
      SchedulingClass                   :   DWORD;
  End;

  PJobObjectBasicUIRestrictions   =   ^TJobObjectBasicUIRestrictions;
  TJobObjectBasicUIRestrictions   =   Record
      UIRestrictionsClass   :   DWORD;
  End;

  PJobObjectEndOfJobTimeInformation   =   ^TJobObjectEndOfJobTimeInformation;
  TJobObjectEndOfJobTimeInformation   =   Record
      EndOfJobTimeAction   :   DWORD;
  End;

  TIOCounters   =   Record   {   all   fields   should   be   actually   unsigned   int64 's   }
      ReadOperationCount     :   Int64;
      WriteOperationCount   :   Int64;
      OtherOperationCount   :   Int64;
      ReadTransferCount       :   Int64;
      WriteTransferCount     :   Int64;
      OtherTransferCount     :   Int64;
  End;

  PJobObjectExtendedLimitInformation   =   ^TJobObjectExtendedLimitInformation;
  TJobObjectExtendedLimitInformation   =   Record
      BasicLimitInformation   :   TJobObjectBasicLimitInformation;
      IoInfo                                 :   TIOCounters;
      ProcessMemoryLimit         :   DWORD;
      JobMemoryLimit                 :   DWORD;
      PeakProcessMemoryUsed   :   DWORD;
      PeakJobMemoryUsed           :   DWORD;
  End;

  PJobObjectSecurityLimitInformation   =   ^TJobObjectSecurityLimitInformation;
  TJobObjectSecurityLimitInformation   =   Record
      SecurityLimitFlags   :   DWORD;
      JobToken                       :   THandle;
      SidsToDisable             :   PTokenGroups;
      PrivilegesToDelete   :   PTokenPrivileges;
      RestrictedSids           :   PTokenGroups;
  End;

  PJobObjectBasicAccountingInformation   =   ^TJobObjectBasicAccountingInformation;
  TJobObjectBasicAccountingInformation   =   Record
      TotalUserTime                           :   TLargeInteger;
      TotalKernelTime                       :   TLargeInteger;
      ThisPeriodTotalUserTime       :   TLargeInteger;
      ThisPeriodTotalKernelTime   :   TLargeInteger;
      TotalPageFaultCount               :   DWORD;
      TotalProcesses                         :   DWORD;
      ActiveProcesses                       :   DWORD;
      TotalTerminatedProcesses     :   DWORD;
  End;

  PJobObjectBasicAndIOAccountingInformation   =   ^TJobObjectBasicAndIOAccountingInformation;
  TJobObjectBasicAndIOAccountingInformation   =   Record
      BasicInfo   :   TJobObjectBasicAccountingInformation;
      IoInfo         :   TIOCounters;
  End;

  PJobObjectBasicProcessIDList = ^TJobObjectBasicProcessIDList;
  TJobObjectBasicProcessIDList = Record
      NumberOfAssignedProcesses : DWORD;
      NumberOfProcessIdsInList : DWORD;
      ProcessIdList : Array[0..0] of ULONG;
  End;

const
  {$IFDEF UNICODE}
  AWSuffix = 'W';
  {$ELSE}
  AWSuffix = 'A';
  {$ENDIF UNICODE}

const
  {for TJobObjectInfoClass }
  JobObjectBasicAccountingInformation                   =   1;
  JobObjectBasicLimitInformation                             =   2;
  JobObjectBasicProcessIdList                                   =   3;
  JobObjectBasicUIRestrictions                                 =   4;
  JobObjectSecurityLimitInformation                       =   5;
  JobObjectEndOfJobTimeInformation                         =   6;
  JobObjectAssociateCompletionPortInformation   =   7;
  JobObjectBasicAndIoAccountingInformation         =   8;
  JobObjectExtendedLimitInformation                       =   9;
  MaxJobObjectInfoClass                                               =   10;


  JOB_OBJECT_ASSIGN_PROCESS = $0001;
  {$EXTERNALSYM JOB_OBJECT_ASSIGN_PROCESS}
  JOB_OBJECT_SET_ATTRIBUTES = $0002;
  {$EXTERNALSYM JOB_OBJECT_SET_ATTRIBUTES}
  JOB_OBJECT_QUERY = $0004;
  {$EXTERNALSYM JOB_OBJECT_QUERY}
  JOB_OBJECT_TERMINATE = $0008;
  {$EXTERNALSYM JOB_OBJECT_TERMINATE}
  JOB_OBJECT_SET_SECURITY_ATTRIBUTES = $0010;
  {$EXTERNALSYM JOB_OBJECT_SET_SECURITY_ATTRIBUTES}
  JOB_OBJECT_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $1F ;
  {$EXTERNALSYM JOB_OBJECT_ALL_ACCESS}


  JOB_OBJECT_TERMINATE_AT_END_OF_JOB                     =   0;
  JOB_OBJECT_POST_AT_END_OF_JOB                               =   1;
  JOB_OBJECT_MSG_END_OF_JOB_TIME                             =   1;
  JOB_OBJECT_MSG_END_OF_PROCESS_TIME                     =   2;
  JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT                   =   3;
  JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO                     =   4;   {   where 's   5?   }
  JOB_OBJECT_MSG_NEW_PROCESS                                     =   6;
  JOB_OBJECT_MSG_EXIT_PROCESS                                   =   7;
  JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS                 =   8;
  JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT                   =   9;
  JOB_OBJECT_MSG_JOB_MEMORY_LIMIT                           =   10;
  JOB_OBJECT_LIMIT_WORKINGSET                                   =   $00000001;
  JOB_OBJECT_LIMIT_PROCESS_TIME                               =   $00000002;
  JOB_OBJECT_LIMIT_JOB_TIME                                       =   $00000004;
  JOB_OBJECT_LIMIT_ACTIVE_PROCESS                           =   $00000008;
  JOB_OBJECT_LIMIT_AFFINITY                                       =   $00000010;
  JOB_OBJECT_LIMIT_PRIORITY_CLASS                           =   $00000020;
  JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME                     =   $00000040;
  JOB_OBJECT_LIMIT_SCHEDULING_CLASS                       =   $00000080;
  JOB_OBJECT_LIMIT_RESERVED1                                     =   $00002000;
  JOB_OBJECT_LIMIT_RESERVED2                                     =   $00004000;
  JOB_OBJECT_LIMIT_RESERVED3                                     =   $00008000;
  JOB_OBJECT_LIMIT_RESERVED4                                     =   $00010000;
  JOB_OBJECT_LIMIT_RESERVED5                                     =   $00020000;
  JOB_OBJECT_LIMIT_RESERVED6                                     =   $00040000;
  JOB_OBJECT_LIMIT_VALID_FLAGS                                 =   $0007FFFF;
  JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS                     =   $000000FF;
  JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS               =   $00001FFF;
  JOB_OBJECT_RESERVED_LIMIT_VALID_FLAGS               =   $0007FFFF;
  JOB_OBJECT_UILIMIT_NONE                                           =   $00000000;
  JOB_OBJECT_UILIMIT_HANDLES                                     =   $00000001;
  JOB_OBJECT_UILIMIT_READCLIPBOARD                         =   $00000002;
  JOB_OBJECT_UILIMIT_WRITECLIPBOARD                       =   $00000004;
  JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS                   =   $00000008;
  JOB_OBJECT_UILIMIT_DISPLAYSETTINGS                     =   $00000010;
  JOB_OBJECT_UILIMIT_GLOBALATOMS                             =   $00000020;
  JOB_OBJECT_UILIMIT_DESKTOP                                     =   $00000040;
  JOB_OBJECT_UILIMIT_EXITWINDOWS                             =   $00000080;
  JOB_OBJECT_UILIMIT_ALL                                             =   $000000FF;
  JOB_OBJECT_UI_VALID_FLAGS                                       =   $000000FF;
  JOB_OBJECT_SECURITY_NO_ADMIN                                 =   $00000001;
  JOB_OBJECT_SECURITY_RESTRICTED_TOKEN                 =   $00000002;
  JOB_OBJECT_SECURITY_ONLY_TOKEN                             =   $00000004;
  JOB_OBJECT_SECURITY_FILTER_TOKENS                       =   $00000008;
  JOB_OBJECT_SECURITY_VALID_FLAGS                           =   $0000000F;
  CREATE_BREAKAWAY_FROM_JOB =  $01000000;
//
// Extended Limits
//

  JOB_OBJECT_LIMIT_PROCESS_MEMORY = $00000100;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_PROCESS_MEMORY}
  JOB_OBJECT_LIMIT_JOB_MEMORY = $00000200;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_JOB_MEMORY}
  JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = $00000400;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION}
  JOB_OBJECT_LIMIT_BREAKAWAY_OK = $00000800;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_BREAKAWAY_OK}
  JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = $00001000;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK}
  JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = $00002000;
  {$EXTERNALSYM JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE}


function CreateJobObjectA(lpJobAttributes: PSecurityAttributes; lpName: LPCSTR): THANDLE; stdcall;
{$EXTERNALSYM CreateJobObjectA}
function CreateJobObjectW(lpJobAttributes: PSecurityAttributes; lpName: LPCWSTR): THANDLE; stdcall;
{$EXTERNALSYM CreateJobObjectW}
function CreateJobObject(lpJobAttributes: PSecurityAttributes; lpName: LPCTSTR): THANDLE; stdcall;
{$EXTERNALSYM CreateJobObject}

function OpenJobObjectA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: LPCSTR): THANDLE; stdcall;
{$EXTERNALSYM OpenJobObjectA}
function OpenJobObjectW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: LPCWSTR): THANDLE; stdcall;
{$EXTERNALSYM OpenJobObjectW}
function OpenJobObject(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: LPCTSTR): THANDLE; stdcall;
{$EXTERNALSYM OpenJobObject}

function AssignProcessToJobObject(hJob, hProcess: THANDLE): BOOL; stdcall;
{$EXTERNALSYM AssignProcessToJobObject}

function TerminateJobObject(hJob: THANDLE; uExitCode: UINT): BOOL; stdcall;
{$EXTERNALSYM TerminateJobObject}

function IsProcessInJob(ProcessHandle, JobHandle: THANDLE; var Result_: BOOL): BOOL; stdcall;
{$EXTERNALSYM IsProcessInJob}

Function QueryInformationJobObject(hJob : THandle;
                        JobObjectInformationClass : TJobObjectInfoClass;
                        lpJobObjectInformation : Pointer;
                        cbJobObjectInformationLength : DWORD;
                        lpReturnLength : PDWORD) : Bool; StdCall;
                        External Kernel32 Name 'QueryInformationJobObject';

Function SetInformationJobObject(hJob : THandle;
                        JobObjectInformationClass : TJobObjectInfoClass;
                        lpJobObjectInformation : Pointer;
                        cbJobObjectInformationLength : DWORD): BOOL; StdCall;
                        External Kernel32 Name 'SetInformationJobObject';

function CreateJobObjectA; external kernel32 name 'CreateJobObjectA';
function CreateJobObjectW; external kernel32 name 'CreateJobObjectW';
function CreateJobObject; external kernel32 name 'CreateJobObject' + AWSuffix;
function OpenJobObjectA; external kernel32 name 'OpenJobObjectA';
function OpenJobObjectW; external kernel32 name 'OpenJobObjectW';
function OpenJobObject; external kernel32 name 'OpenJobObject' + AWSuffix;
function AssignProcessToJobObject; external kernel32 name 'AssignProcessToJobObject';
function TerminateJobObject; external kernel32 name 'TerminateJobObject';
function IsProcessInJob; external kernel32 name 'IsProcessInJob';

implementation

end.

All credits go to Linas, - StackOverFlaw, the original creator

0 comments:

Post a Comment