tags:

views:

671

answers:

4

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.)

A: 

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 - TeamB
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
+2  A: 

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
A: 

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 - TeamB
+4  A: 

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.

RichAmberale
Very clever... and it looks like you can actually use cmd.exe as the intermediate process: Process.Start("cmd.exe", "/c start iexplore.exe <url>"); Nice answer, and a lot simpler than mine! :-)
Eric Rosenberger
That does indeed work; unfortunately it didn't resolve my original problem... :-/