unit WindowsJob
platform;
interface uses Winapi.Windows, WinAPI_Job_Header;
type
/// <summary>
/// Repräsentiert einen Windows-Job- Siehe
/// <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684161(v=vs.85).aspx">
/// MSDN: Job Objects
/// </see>.
/// </summary>
TWinJob =
class
public type
TProcessHandle =
Winapi.Windows.THandle;
TJobHandle =
Winapi.Windows.THandle;
private
/// <seealso cref="KillContainingProcessesOnExit" />
FKillContainingProcessesOnExit: Boolean;
protected var
/// <seealso cref="JobHandle" />
FJobHandle: TJobHandle;
/// <remarks>
/// Wird kein Name verwendet entspricht der Wert des Feldes
/// <c>EmptyStr</c>
/// </remarks>
jobName:
String;
/// <summary>
/// Enthält die
/// <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684156(v=vs.85).aspx">
/// JOBOBJECT_EXTENDED_LIMIT_INFORMATION
/// </see>-Informationen des Jobs. Wird von
/// <see cref="queryJobInformation" /> abgefragt.
/// </summary>
/// <seealso cref="KillContainingProcessesOnExit" />
extInfo: TJobObjectExtendedLimitInformation;
protected
/// <summary>
/// Prüft, ob das in <see cref="JobHandle" /> abgelegte
/// Handle auf eine fehlgeschlagene Job-Erstellung hindeutet.
/// In diesem Fall wird eine <c>EOSError</c>-Exception
/// geworfen
/// </summary>
/// <exception cref="EOSError" />
procedure checkJobHandle();
/// <summary>
/// Aktualisiert die <c>ExtendedLimitInformation</c> dieses
/// Jobs und legt diese im Feld
/// <see cref="extInfo" /> ab.
/// </summary>
procedure queryJobInformation();
virtual;
procedure setKillContainingProcessesOnExit(
const Value: Boolean);
public
constructor Create();
overload;
/// <exception cref="EOSError">
/// Wenn bereits ein event, semaphore, mutex, waitable timer oder
/// file-mapping mit dem gleichen Namen existiert
/// </exception>
constructor Create(
const jobName:
String);
overload;
destructor Destroy();
override;
/// <returns>
/// Gibt an ob der Prozess erfolgreich in diesen Job
/// verschoben werden konnte
/// </returns>
/// <remarks>
/// Der mit <c>CreateProcess</c> erstellte Prozess muss mit dem
/// <see cref="WinAPI_Job_Header.CREATE_BREAKAWAY_FROM_JOB" />-Flag
/// in seinem <c>dwCreationFlags</c>-Parameter erstellt werden.
/// Ansonsten schlägt die Methode fehl und gibt <c>False</c> zurück
/// </remarks>
function moveProcessTo(
const processHandle: TProcessHandle): Boolean;
public // properties
/// <summary>
/// Gibt an ob die im Job enthaltenen Prozesse <b>beim Schließen
/// des letzten Handles auf den Job</b> vom Betriebssystem
/// terminiert werden sollen
/// </summary>
property KillContainingProcessesOnExit: Boolean
read FKillContainingProcessesOnExit
write setKillContainingProcessesOnExit;
property JobHandle: TJobHandle
read FJobHandle;
end;
implementation uses System.SysUtils;
{ TWinJob }
constructor TWinJob.Create();
begin
inherited Create();
FJobHandle := CreateJobObject(
nil,
nil);
jobName := EmptyStr;
checkJobHandle();
end;
procedure TWinJob.checkJobHandle();
var
lastError: DWORD;
begin
if (jobHandle = 0)
then begin
lastError := GetLastError();
case lastError
of
ERROR_INVALID_HANDLE:
raise {$REGION 'EOSError'}
EOSError.Create(
'
An event, semaphore, mutex, waitable timer, or file-mapping '
+'
with the same name of "'+jobName+'
" already '
+'
exists. Cannot create Job.'
);
{$ENDREGION 'EOSError'}
else
SetLastError(lastError);
RaiseLastOSError();
end;
end;
end;
constructor TWinJob.Create(
const jobName:
String);
begin
inherited Create();
self.jobName := jobName;
FJobHandle := CreateJobObject(
nil, PChar(jobName));
checkJobHandle();
end;
destructor TWinJob.Destroy();
begin
CloseHandle(jobHandle);
inherited;
end;
function TWinJob.moveProcessTo(
const processHandle: TProcessHandle): Boolean;
begin
Result := AssignProcessToJobObject(jobHandle, processHandle);
end;
procedure TWinJob.queryJobInformation();
begin
Win32Check(
QueryInformationJobObject(
jobHandle,
TJobObjectInfoClass.ExtendedLimitInformation,
Addr(extInfo),
SizeOf(extInfo),
nil
)
);
end;
procedure TWinJob.setKillContainingProcessesOnExit(
const Value: Boolean);
const
queryFirst: Boolean = True;
var
basicInfo: TJobObjectBasicLimitInformation;
begin
FKillContainingProcessesOnExit := Value;
if queryFirst
then queryJobInformation();
basicInfo := extInfo.BasicLimitInformation;
if KillContainingProcessesOnExit
then
basicInfo.LimitFlags := basicInfo.LimitFlags
or JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
else
basicInfo.LimitFlags := basicInfo.LimitFlags
and (
not JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE);
extInfo.BasicLimitInformation := basicInfo;
Win32Check(
SetInformationJobObject(
jobHandle,
TJobObjectInfoClass.ExtendedLimitInformation,
Addr(extInfo),
SizeOf(extInfo)
)
);
end;
end.