【问题标题】:Process.Start() and the Process TreeProcess.Start() 和进程树
【发布时间】:2010-11-05 07:59:54
【问题描述】:

如何使用 Process.Start(),但启动的进程与启动进程不在同一进程树中?

考虑这个示例控制台应用程序:

using System;
using System.Diagnostics;
using System.Threading;

internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("Starting ie...");
        Process.Start("c:\\Program Files\\Internet Explorer\\iexplore.exe", "http://www.google.com");
        Console.WriteLine("Waiting for 15 seconds");
        Thread.Sleep(15000);
        Console.WriteLine("Exiting...");
    }
}

当此程序正常退出时,Internet Explorer 将继续运行。但是,如果在 15 秒的睡眠期间您进入任务管理器并选择此程序并选择“结束进程树”,Internet Explorer 也会关闭。

(这与my question from earlier today直接相关,目前还没有回复。在Windows XP中,当屏幕保护程序结束时,它似乎结束了进程树,而在Vista中,只是屏幕保护程序进程结束。)

【问题讨论】:

标签: c# .net


【解决方案1】:

您必须分离子进程。不知道如何在 c# 中进行,但考虑下面的 C++ 代码,您可以使用 /P:invoke 在.net 中实现相同的代码。

BOOL fSuccess = CreateProcess(..., &pi);
if (fSuccess) {

// Allow the system to destroy the process & thread kernel
// objects as soon as the child process terminates.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}

【讨论】:

    【解决方案2】:

    Eric 是正确的:Windows 没有公开任何更改父进程的方法。但是,如果父母去世,则没有返回祖父母的链接,因此您可以通过一个启动孩子然后死亡的中间过程来实现您的目标。

    所以:Proc A 启动 proc B,然后 proc B 启动 proc C 并立即死亡。当 proc B 死亡时,proc C 将成为进程树上的根节点 - 在 proc B 死亡后,proc C 将不在 proc A 的树中。

    【讨论】:

    • 非常聪明...而且看起来您实际上可以使用 cmd.exe 作为中间进程: Process.Start("cmd.exe", "/c start iexplore.exe " );很好的答案,比我的简单得多! :-)
    • 确实有效;不幸的是,它并没有解决我原来的问题... :-/
    • 为什么Process.GetProcessByName() 与 B 分离后无法再获得“proc C”?
    【解决方案3】:

    我不相信 Windows 公开(通过 .NET 或其他方式)任何更改进程父级的方法。

    作为替代方案,您可以在系统启动时运行一个单独的进程(例如通过“SOFTWARE/Microsoft/Windows/CurrentVersion/Run”注册表项),并让触发应用程序(您的屏幕保护程序)使用进程间通信(SendMessage 等)告诉单独的进程启动浏览器。然后,单独的进程将成为父进程,并且当屏幕保护程序的进程树被杀死时,浏览器不会被杀死。


    这是一些示例代码。请注意,这不会进行任何错误检查,而且我还没有在实际屏幕保护程序的上下文中对其进行测试,但它应该让您了解所涉及的内容:

    在屏保类中:

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint RegisterWindowMessage(string lpString);
    
    [DllImport("user32.dll")]
    static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);   
    
    private uint message;
    

    在屏保的初始化代码中:

    message = RegisterWindowMessage("LaunchBrowser");
    

    在屏保的浏览器启动代码中:

    SendMessage(FindWindow(null, "BrowserLauncher"), message, UIntPtr.Zero, IntPtr.Zero);
    

    在单独进程的表单类中:

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint RegisterWindowMessage(string lpString);
    
    private uint message;
    

    在单独进程的 Form_Load 代码中:

    message = RegisterWindowMessage("LaunchBrowser");
    Text = "BrowserLauncher";
    

    并覆盖单独进程的表单的 WndProc:

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == message)
        {
            Process.Start("iexplore.exe", "http://www.google.com");
        }
    
        base.WndProc(ref m);
    }
    

    (当然,您需要隐藏单独进程的表单。)

    【讨论】:

      【解决方案4】:

      在调用 Process.Start() 之前尝试将 Process.StartInfo.UseShellExecute 设置为 False(默认为 True)。这样,CreateProcess() 在内部使用而不是 ShellExecute()。

      【讨论】:

        【解决方案5】:

        据我所知, Process.Start() 不支持您的要求。您必须使用 PInvoke 直接调用 Win32 API CreateProcess() 函数,以便您可以在其 dwCreationFlags 参数中指定 DETACHED_PROCESS 标志。

        【讨论】:

        • DETACHED_PROCESS 导致子进程不附加到父进程的控制台窗口;它与进程树无关。
        猜你喜欢
        • 1970-01-01
        • 2011-09-17
        • 1970-01-01
        • 2019-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多