Neither proposed outcome is guaranteed by the published Win32 API contract when the creating process terminates before CreateProcessW returns; code requiring that guarantee must place process creation and Job ownership in the long-lived supervisor itself.
PROC_THREAD_ATTRIBUTE_JOB_LIST specifies that the listed Job handles are assigned to the child in list order, but the documentation does not define CreateProcessW as a transaction, nor does it specify cancellation, rollback, or all-or-nothing behavior when the caller disappears during the call.^1^
Supported contract
For an uninterrupted call:
- A nonzero return means process creation succeeded, and
PROCESS_INFORMATIONreceives the new process and initial-thread handles and identifiers. - Zero means the call failed;
GetLastErrorsupplies the failure status. - A successful return does not mean application initialization completed.
CreateProcessWreturns before initialization finishes, and a required-DLL failure can subsequently terminate the process.^2^ - With
PROC_THREAD_ATTRIBUTE_JOB_LIST, the child is to be assigned to the listed Jobs in the specified order.^1^
The Job-list attribute is supported only on Windows 10 or later and Windows Server 2016 or later. Its backing Job-handle array must remain valid until the attribute list is destroyed.^1^
Interrupted-call boundary
The documented contract does not state what happens if the caller is terminated while CreateProcessW is in progress. In particular, it does not promise:
- cancellation before the child can execute;
- completion of every requested Job association;
- rollback of associations already made;
- termination of a partially created process;
- delivery of
PROCESS_INFORMATIONelsewhere; or - an externally discoverable transaction identifier.
Consequently, the documentation does not provide a finite authoritative list of “permitted outcomes.” A child may not exist, may already have terminated, or may exist after some internal creation work; however, treating any specific intermediate state—including partial Job membership—as supported behavior would rely on implementation details rather than the API contract.
Job association itself is persistent once established: after a process is associated with a Job, that association cannot be broken. Multiple associations require a valid nested-Job hierarchy; nested Jobs were introduced in Windows 8 and Windows Server 2012.^3^ This permanence still does not document whether PROC_THREAD_ATTRIBUTE_JOB_LIST rolls back earlier assignments if a later assignment or the creation operation is interrupted.
Reliable containment design
Move the CreateProcessW call into the independent supervisor that already owns the Job handles. Keep that supervisor alive for the complete operation and, preferably, for the contained process’s lifetime. This gives it the returned process handle on success rather than requiring reconstruction after the creator dies.
If the supervisor must ensure that processes already admitted to a Job are terminated when containment ownership disappears:
- Keep the controlling Job handle in the supervisor.
- Configure
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. - Ensure no unintended process retains another handle to that Job.
Closing the last Job handle then terminates associated processes; without that limit, a Job is not destroyed until its last handle is closed and all associated processes have terminated.^3^ This protects processes that actually reached Job membership; it cannot extend the undocumented interruption semantics of process creation.
Can another supervisor recover the child’s identity?
It can observe current Job membership, but it cannot reliably reconstruct the unique process object from a PID alone:
-
QueryInformationJobObject(..., JobObjectBasicProcessIdList, ...)returns the Job’s current process-ID list. For nested Jobs, the list includes processes in child Jobs.^4^ - The buffer may be incomplete when
NumberOfProcessIdsInListis less thanNumberOfAssignedProcesses; resize and retry.^5^ - A Job completion port can report
JOB_OBJECT_MSG_NEW_PROCESSwith a PID, but ordinary Job messages are not guaranteed. Moreover, a reported PID may already have terminated and been recycled unless an open process handle is retained.^6^
Therefore, neither Job enumeration nor completion-port notification is a reliable substitute for the process handle that CreateProcessW would have returned. The robust architecture is for the long-lived supervisor to perform creation and retain that handle. If an authoritative guarantee for caller death during the system call is required, it would need an explicit Microsoft API-contract clarification; the public documentation currently does not define one.
References