9

How can I use Process.Start(), but have the launched process not in the same process tree as the launching process?

Consider this sample console application:

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...");
    }
}

When this program exits normally, Internet Explorer will continue to run. However, if during the 15 second sleep period you go to Task Manager and select this program and select "End Process Tree", Internet Explorer will also close.

(This is directly related to my question from earlier today that, as yet, has no replies. In Windows XP, when the screen saver process ends, it appears to end the process tree, whereas in Vista, just the screen saver process is ended.)

Community
  • 1
  • 1
user127665
  • 105
  • 1
  • 1
  • 6
  • Have you resolved this issue? I have the same problem...http://stackoverflow.com/questions/8364671/screensaver-does-not-restart-after-it-launches-an-external-process – robob Dec 03 '11 at 06:52

5 Answers5

19

Eric is correct: Windows does not expose any method of changing a processes parent. However, if the parent dies, there is no link back to grandparent so you can achieve your goal with an intermediate process that starts the child, then dies.

So: Proc A starts proc B, then proc B starts proc C and immediately dies. When proc B dies, proc C will be a root node on the process tree - proc C will NOT be in proc A's tree after proc B dies.

noctonura
  • 12,763
  • 10
  • 52
  • 85
  • 6
    Very clever... and it looks like you can actually use cmd.exe as the intermediate process: Process.Start("cmd.exe", "/c start iexplore.exe "); Nice answer, and a lot simpler than mine! :-) – Eric Rosenberger Jun 24 '09 at 04:13
  • That does indeed work; unfortunately it didn't resolve my original problem... :-/ – user127665 Jun 24 '09 at 05:18
  • Why `Process.GetProcessByName()` can't get the "proc C" anymore after it has been detached from B? – Altiano Gerung Jul 31 '18 at 07:16
5

Try setting Process.StartInfo.UseShellExecute to False (it is True by default) before calling Process.Start(). That way, CreateProcess() is used internally instead of ShellExecute().

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

I don't believe Windows exposes (via .NET or otherwise) any method of changing a process's parent.

As an alternative, you could run a separate process at system startup (via the "SOFTWARE/Microsoft/Windows/CurrentVersion/Run" registry key for example), and have the triggering application (your screen saver) use inter-process communication (SendMessage or the like) to tell the separate process to launch the browser. Then the separate process would be the parent and the browser wouldn't be killed when the screen saver's process tree is killed.


Here's some example code. Note that this doesn't do any error checking, and I haven't tested it in the context of an actual screen saver, but it should give you an idea of what's involved:

In the screen saver class:

[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;

In the screen saver's initialization code:

message = RegisterWindowMessage("LaunchBrowser");

In the screen saver's browser launching code:

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

In the separate process's form class:

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

private uint message;

In the separate process's Form_Load code:

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

And override the separate process's form's WndProc:

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

    base.WndProc(ref m);
}

(You'll want to make the separate process's form hidden, of course.)

Eric Rosenberger
  • 8,987
  • 1
  • 23
  • 24
0

As far as I know, Process.Start() does not support what you are asking for. You would have to use PInvoke to call the Win32 API CreateProcess() function directly so that you can specify the DETACHED_PROCESS flag in its dwCreationFlags parameter.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    DETACHED_PROCESS causes the child process to not be attached to the parent process's console window; it is not related to the process tree. – Eric Rosenberger Jun 23 '09 at 22:01
0

You have to detach the child process. Not sure how to go about in c# but consider the below code in C++, with which you can use /P:invoke to implement the same in .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);
}
Gomes
  • 3,330
  • 25
  • 17