In my C# program, I need to (1) create a process in a suspended state and (2) modify the token. I am doing this so that I can register it to a JobObject so that if the parent process dies unexpectedly while the child is still running, the child is killed. I need to modify the token per business requirements.
I figured out that it is not possible to add an un-started C# Process to a JobObject, or create a Process in a suspended state, or modify its token. So, I am using C++ CreateProcessAsUser to accomplish these things. So far, so good:
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
public static uint CreateSuspendedProcess(string appPath, string cmdLine = null, bool visible = false)
result = CreateProcessAsUser(
hUserToken, // Modified token - working great
appPath, // Application Name - doesn't end up in Process.ProcessStartInfo?
cmdLine, // is null
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags, // contains CREATE_SUSPENDED
pEnv,
null, // working directory
ref startInfo, // not related to C# ProcessStartInfo?
out procInfo);
// does other stuff, and then returns the Process ID
Where I'm having a problem is actually starting the process.
From the caller (see snippet below), I attempt to get the process from the ID of the process I created with CreateProcessAsUser. The process made by CreateProcessAsUser doesn't have StartInfo containing the filename. I expected appPath to function the same as StartInfo.FileName, but there is something I do not understand here because process.StartInfo seems to be dummy info, per the documentation I read here that says GetProcesses, etc., will not return StartInfo and StartInfo cannot be modified once a process has started. I suppose creating a process in a suspended state counts as starting.
I'm a rookie developer, and have never used C++ before attempting to muddle through this, but what I have been able to learn is that maybe I shouldn't be trying to do Process.Start - but I haven't found any alternatives that satisfy the need to register the process to a JobObject.
var processId = CreateSuspendedProcess(startInfo.FileName, startInfo.Arguments);
IntPtr hProcess = IntPtr.Zero;
using (var job = new Job())
using (var process = Process.GetProcessById((int)processId)) // use the suspended process
{
var jobSuccess = job.AddProcess(process.Id); // this works
if (!jobSuccess)
{
// do not proceed if process cannot be run in job object; dispose of both
throw new Exception("Failed to add process to job object.");
}
// === I want the start info to have the filename, but can't except in Debug
//process.StartInfo.FileName = startInfo.FileName;
// other code here
Process.Start(); // Get a no FileName error