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 a开发者_C百科s 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.
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.
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.
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();
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);
}
}
}
精彩评论