如果生成进程(父进程)在生成进程(子进程)结束之前结束,则父子链被破坏。要使用它,您必须使用像这样的中间存根进程:
Caller.exe → Stub.exe → File.exe。
这里的 Stub.exe 是简单的启动程序,它在启动 File.exe 后立即结束。
如果你启动一个进程,那么你将成为它的父进程。
也许你可以尝试从 cmd.exe 启动你的进程,所以 cmd.exe 将成为父进程。
Process proc = Process.Start(new ProcessStartInfo { Arguments = "/C explorer", FileName = "cmd", WindowStyle = ProcessWindowStyle.Hidden });
我一直在尝试启动一个更新程序进程,该进程删除调用进程的文件并用新文件替换它们。通过设置
UseShellExecute = true
,我能够在调用进程退出时避免衍生进程退出。
这是在使用 WPF 的 .Net Core 3.0 应用程序中。
var startInfo = new ProcessStartInfo("Updater.exe");
startInfo.UseShellExecute = true;
Process.Start(startInfo);
Environment.Exit(0);
这会在没有父进程的情况下运行新进程:
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);
Process.Start(string fileName)
的文档说
a new process that’s started alongside already running instances
of the same process will be independent
它说
Starting a process by specifying its file name is similar to
typing the information in the Run dialog box of the Windows Start menu
在我看来这与独立流程一致。
所以根据文档,
Process.Start
应该做你想做的。
System.Diagnostics.Process.Start()
方法在幕后调用 kernel32!CreateProcess()
。使用 kernel32!CreateProcess()
创建进程时,您可以使用进程属性指定不同的父进程。这是一个用 C++ 编写的函数,它就是这样做的——尽管我不确定 .Net 如何支持这些功能。
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;
}
源代码取自https://scorpiosoftware.net/2021/01/10/parent-process-vs-creator-process/
我正在使用这段代码。优点是您不必使用 PInvoke。我发现带有
CreateProcess
标志的 DETACHED_PROCESS
api 不是 100% 可靠的,子进程有时仍然会在父进程死亡时死亡。
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
这是我现在使用的代码。我认为这可能对某人有用。它接受一个参数。该参数是一个 base64 编码的字符串,可解码为您要运行的文件的路径。
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