2

I have a need to launch multiple browser instances / tabs in succession but using Process.Start(url) I am finding that not every URL is launched.

    for (int i = 0; i < n; i++)
    {
        var p = Process.Start("http://localhost/#" + i.ToString());
    }

For any value n > 1 I find that only the last URL is opened in the browser (I'm testing with IE as the default browser). If I add a Thread.Sleep(1000) after Process.Start then I see a variety of behavior: sometimes all n are created; sometimes a subset.

The following does work as expected, but assumes IE (would rather use the default browser) and launches n browser instances rather than n tabs:

    for (int i = 0; i < n; i++)
    {
        var p = Process.Start("iexplore.exe", "http://localhost/#" + i.ToString());
    }

I would prefer to get it working with the default browser behavior, but need it to be deterministic.

alexdej
  • 3,751
  • 23
  • 21

4 Answers4

0

Seems to be a windows explorer bug. You could try to find to common browser executables and use them if present, if no match is found you can fall back to your default method.

codymanix
  • 28,510
  • 21
  • 92
  • 151
0

Also see Open multiple-tabs in IE9 using Process (possible duplicate)

We are working on exactly this issue for IE9. This seems to work.

bool isLaunched = false;
foreach (string url in urls)
{
    try
    {
        if (!isLaunched)
        {
            Process p = new Process();
            p.StartInfo.FileName = browser;
            p.StartInfo.Arguments = url;
            p.Start();
            Thread.Sleep(1000);
            isLaunched = true;
        }
        else
        {
            Process.Start(url);
        }
    }
    catch (Exception ex)
    {
// something

    }
}

In my experience p.WaitForIdle does not work. Also, there are differences in behavior if a browser instance is already open on the machine. Calling iexplore actually passes the url to the open instance and exits your called process.

Community
  • 1
  • 1
tofutim
  • 22,664
  • 20
  • 87
  • 148
  • Yes this is consistent with my observation: adding Thread.Sleep(5000) after the first Process.Start makes this much more stable, though I imagine that would break down on an especially slow/busy machine. Incidentally where did you get the value for 'browser'? I'm considering looking up HKCR\http\shell\open\command and using that. – alexdej Jun 01 '11 at 22:22
  • That itself is another ball of wax. In general you can get it from the registry, but some newer Win7 machines there seem to be some issues. At the moment using key = Registry.CurrentUser.OpenSubKey(@"Software\Clients\StartMenuInternet", false); – tofutim Jun 01 '11 at 22:27
  • See http://msdn.microsoft.com/en-us/library/dd203067(v=vs.85).aspx - does the answer work on your end? – tofutim Jun 01 '11 at 22:32
0

I'm completely winging this one, but here it goes. Derived from @tofutim's answer. Basically, use locking and monitor the Process state with a Timer. This of course is highly dependent on being able to determine if the process has successfully started, which is being tested with

if (p.HasExited == false && p.Id != 0){ }

The full solution:

bool isLaunching = false;
object launchingLock = new object();
Process p = null;

Timer timer = new Timer(new TimerCallback((state) =>
{
    lock (launchingLock)
    {
        if (isLaunching)
        {
            if (p != null && p.HasExited == false && p.Id != 0)
            {
                // Success! 
                isLaunching = false;
                Monitor.PulseAll(launchingLock);
            }
            else if(p != null && p.HasExited)
            {
                // Some sort of failure code/notification?
                // We still want to pulse though, otherwise thread will be forever kept waiting
                isLaunching = false;
                Monitor.PulseAll(launchingLock);
            }
        }
    }
}));

timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));

foreach (string url in urls)
{
    try
    {
        lock (launchingLock)
        {
            isLaunching = true;
            p = new Process();
            p.StartInfo.FileName = browser;
            p.StartInfo.Arguments = url;
            p.Start();

            // Wait until the process has finished starting before moving on
            Monitor.Wait(launchingLock);
        }
    }
    catch (Exception ex)
    {
        // something
    }
}

timer.Dispose();
Matt
  • 41,216
  • 30
  • 109
  • 147
0

Ended up with the following, still not ideal (launches multiple browser windows), incorporating the command-line arg parsing solution Split string containing command-line parameters into string[] in C#

const int n = 3;
static void Main(string[] args)
{
    for (int i = 0; i < n; i++)
    {
        var start = GetDefaultBrowserStartInfo("http");
        start.Arguments += string.Format(" http://localhost/#{0}", i);

        var p = Process.Start(start);
    }
}

static ProcessStartInfo GetDefaultBrowserStartInfo(string scheme)
{
    string command = null;
    using (var key = Registry.ClassesRoot.OpenSubKey(string.Format(@"{0}\shell\open\command", scheme)))
    {
        command = (string)key.GetValue("");
    }

    string[] parsed = CommandLineToArgs(command);
    return new ProcessStartInfo
    {
        FileName = parsed[0],
        Arguments = string.Join(" ", parsed.Skip(1).ToArray()),
    };
}

[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW(
    [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);

static string[] CommandLineToArgs(string commandLine)
{
    int argc;
    var argv = CommandLineToArgvW(commandLine, out argc);
    if (argv == IntPtr.Zero)
        throw new System.ComponentModel.Win32Exception();
    try
    {
        var args = new string[argc];
        for (var i = 0; i < args.Length; i++)
        {
            var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
            args[i] = Marshal.PtrToStringUni(p);
        }

        return args;
    }
    finally
    {
        Marshal.FreeHGlobal(argv);
    }
}

}

Community
  • 1
  • 1
alexdej
  • 3,751
  • 23
  • 21