How would I go about starting a new process without it being the child of the calling process.
Example:
Main Program (Caller.exe)
process.start("file.exe")
Image:
How would I go about starting a new process without it being the child of the calling process.
Example:
Main Program (Caller.exe)
process.start("file.exe")
Image:
If the spawning process (parent) ends before the spawned process (child) does, then the parent-child chain is broken. To make use of this, you'd have to use an intermediate stub-process like so:
Caller.exe → Stub.exe → File.exe.
Here Stub.exe is simple launcher program that ends just after starting File.exe.
I have been trying to start a updater process which deletes the files of the calling process and replaces them with new ones. By setting UseShellExecute = true
, I was able to circumvent the spawned process from exiting when the calling process exited.
This is inside a .Net Core 3.0 application using WPF.
var startInfo = new ProcessStartInfo("Updater.exe");
startInfo.UseShellExecute = true;
Process.Start(startInfo);
Environment.Exit(0);
If you start a process, then you'll be its parent.
Maybe you could try to start your process from cmd.exe instead, so cmd.exe will be the parent.
Process proc = Process.Start(new ProcessStartInfo { Arguments = "/C explorer", FileName = "cmd", WindowStyle = ProcessWindowStyle.Hidden });
This runs new process without parent:
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = @"cmd";
psi.Arguments = "/C start notepad.exe";
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process.Start(psi);
The documentation of Process.Start(string fileName)
says
a new process that’s started alongside already running instances
of the same process will be independent
and it says
Starting a process by specifying its file name is similar to
typing the information in the Run dialog box of the Windows Start menu
which to me seems consistent with independent processes.
So according to the documentation, Process.Start
should do what you desire.
I am using this code. The advantage is you don't have to use PInvoke. I have found that the CreateProcess
api with the DETACHED_PROCESS
flag is not 100% reliable, the child process still sometimes dies when the parent dies.
https://github.com/dahall/taskscheduler
// you need two variables:
string fileName = ...
string arguments = ...
// Get the task service on the local machine
using TaskService ts = new();
// create task name
var taskName = "DetachedProcess_" + Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(Path.GetFileName(fileName))));
// remove the task if it already exists
ts.RootFolder.DeleteTask(taskName, false);
// create a new task definition and assign properties
TaskDefinition td = ts.NewTask();
td.RegistrationInfo.Description = "Detached process for " + fileName;
// create a trigger that will run the process in 5 seconds
td.Triggers.Add(new TimeTrigger(IPBanService.UtcNow.AddSeconds(5.0)));
// create the action to run the process
td.Actions.Add(new ExecAction(fileName, arguments, Path.GetDirectoryName(fileName)));
// delete task upon completion
td.Actions.Add(new ExecAction("schtasks.exe", "/Delete /TN \"" + taskName + "\" /F", null));
// register the task in the root folder
var task = ts.RootFolder.RegisterTaskDefinition(taskName, td);
task.Run(); // just run it now
System.Diagnostics.Process.Start()
method calls kernel32!CreateProcess()
under the hood. When creating a process with kernel32!CreateProcess()
you can specify a different parent by using a process attribute. Here is a function written in C++ that does just that - although I'm not sure how .Net supports such features.
bool CreateProcessWithParent(DWORD parentId, PWSTR commandline) {
auto hProcess = ::OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parentId);
if (!hProcess)
return false;
SIZE_T size;
//
// call InitializeProcThreadAttributeList twice
// first, get required size
//
::InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
//
// now allocate a buffer with the required size and call again
//
auto buffer = std::make_unique<BYTE[]>(size);
auto attributes = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(buffer.get());
::InitializeProcThreadAttributeList(attributes, 1, 0, &size);
//
// add the parent attribute
//
::UpdateProcThreadAttribute(attributes, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), nullptr, nullptr);
STARTUPINFOEX si = { sizeof(si) };
//
// set the attribute list
//
si.lpAttributeList = attributes;
PROCESS_INFORMATION pi;
//
// create the process
//
BOOL created = ::CreateProcess(nullptr, commandline, nullptr, nullptr,
FALSE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
(STARTUPINFO*)&si, &pi);
//
// cleanup
//
::CloseHandle(hProcess);
::DeleteProcThreadAttributeList(attributes);
return created;
}
Source code taken from https://scorpiosoftware.net/2021/01/10/parent-process-vs-creator-process/
Here is the code that I'm now using. I thought that it may be useful to someone. It accepts one argument. The argument is a base64 encoded string that decodes to the path of the file that you would like to run.
Module Module1
Sub Main()
Dim CommandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String) = My.Application.CommandLineArgs
If CommandLineArgs.Count = 1 Then
Try
Dim path As String = FromBase64(CommandLineArgs(0))
Diagnostics.Process.Start(path)
Catch
End Try
End
End If
End Sub
Function FromBase64(ByVal base64 As String) As String
Dim b As Byte() = Convert.FromBase64String(base64)
Return System.Text.Encoding.UTF8.GetString(b)
End Function
End Module