Loading webbrowser in seperate threads

  • Thread starter Thread starter amdrit
  • Start date Start date
A

amdrit

I have a need to create an instance of the webbrowser control in seperate
threads and display each on seperate forms in an MDI application. The
hosting site knows if the browser is in the same thread and will not isolate
the sessions for me. I can open seperate instances of IE or Netscape and
the site works fine, however it will not work fine in tabbed pages. So I
want to be able to have a singular application with mutliple browsers.

I think that I am creating the instance of my usercontrol in the wrong
manner but am unable to figure out what I need to change about it.

I am using C#'08

Thanks in advance and Happy Holidays

What I have done is:

project1 the dll

1. Create an interface to interact with the webbrowser control

public interface IMyBrowser
{
void StartUp();
}

2. Create a custom usercontrol that hosts the webbrowser control that
implements the interface

public class MyBrowser: UserControl, IMyBrowser
{
public void StartUp()
{
this.browser.Navigate(@MyWebsite);
}
}

Project2 the executable

1. Added a reference to the dll
2. Created a mdi form to host the mdi children
void MenuItemClick(...)
{
frmBrowser f = new frmBrowser();
f.mdiParent = this;
f.Show();
}

3. Created a mdi child form to host the browser control from the dll
public void frmBrowser()
{
InitializeComponent();
MyDll.MyBrowser instance =
(MyDll.MyBrowser)GetInstance<MyDll.IMyBrowser>();
if(instance != null)
{
this.controls.add(instance);
instance.dock = DockStyle.Fill;
instance.StartUp();
}
}

private object GetInstance<T>()
{
object instance = null;

string[] files =
Directory.GetFiles(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"*.dll");

foreach (string file in files)
{
Trace.WriteLine("Loading " + file);
try
{
Assembly assembly = Assembly.LoadFile(file);
foreach (Type type in assembly.GetTypes())
{
if (!type.IsClass || type.IsNotPublic) continue;
Type[] interfaces = type.GetInterfaces();
if (((IList)interfaces).Contains(typeof(T)))
{
instance = Activator.CreateInstance(type);
break;
}
}
}
catch (Exception ex)
{
//throw;
}
}
return instance;
}
 
I have a need to create an instance of the webbrowser control in seperate
threads and display each on seperate forms in an MDI application. The
hosting site knows if the browser is in the same thread and will not isolate
the sessions for me. I can open seperate instances of IE or Netscape and
the site works fine, however it will not work fine in tabbed pages. So I
want to be able to have a singular application with mutliple browsers.

I think that I am creating the instance of my usercontrol in the wrong
manner but am unable to figure out what I need to change about it.

I am using C#'08

Thanks in advance and Happy Holidays

What I have done is:

project1 the dll

1. Create an interface to interact with the webbrowser control

public interface IMyBrowser
{
void StartUp();
}

2. Create a custom usercontrol that hosts the webbrowser control that
implements the interface

public class MyBrowser: UserControl, IMyBrowser
{
public void StartUp()
{
this.browser.Navigate(@MyWebsite);
}
}

Project2 the executable

1. Added a reference to the dll
2. Created a mdi form to host the mdi children
void MenuItemClick(...)
{
frmBrowser f = new frmBrowser();
f.mdiParent = this;
f.Show();
}

3. Created a mdi child form to host the browser control from the dll
public void frmBrowser()
{
InitializeComponent();
MyDll.MyBrowser instance =
(MyDll.MyBrowser)GetInstance<MyDll.IMyBrowser>();
if(instance != null)
{
this.controls.add(instance);
instance.dock = DockStyle.Fill;
instance.StartUp();
}
}

private object GetInstance<T>()
{
object instance = null;

string[] files =
Directory.GetFiles(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"*.dll");

foreach (string file in files)
{
Trace.WriteLine("Loading " + file);
try
{
Assembly assembly = Assembly.LoadFile(file);
foreach (Type type in assembly.GetTypes())
{
if (!type.IsClass || type.IsNotPublic) continue;
Type[] interfaces = type.GetInterfaces();
if (((IList)interfaces).Contains(typeof(T)))
{
instance = Activator.CreateInstance(type);
break;
}
}
}
catch (Exception ex)
{
//throw;
}
}
return instance;
}

A couple of questions:

1. If creating these browsers in a new thread is the goal, then where is
your code to start them in a new thread?

2. Why the complicated reflection code to create the instance? If two
classes in the dll implement the interface, there is no guarantee which
class you will get.
 
I suppose this is where I am lost. MDI Children have to be on the same
thread as the MDI Parent as far as I can tell. I was hoping to be able to
create an instance of my usercontrol in seperate appdomains as a work
around.

I understand that if my dll has two classes that implement the interface
I'll get random results. If I were to encounter that I'd just have to make
a better solution. I only plan on having the one usercontrol ever, I was
just looking for the means to isolate the intances.

So the underlying question remains: How do I create seperate instanecs of
the webbrowser control in a single application?

I just didn't want to come empy handed to the conversation.


Family Tree Mike said:
I have a need to create an instance of the webbrowser control in seperate
threads and display each on seperate forms in an MDI application. The
hosting site knows if the browser is in the same thread and will not
isolate
the sessions for me. I can open seperate instances of IE or Netscape and
the site works fine, however it will not work fine in tabbed pages. So I
want to be able to have a singular application with mutliple browsers.

I think that I am creating the instance of my usercontrol in the wrong
manner but am unable to figure out what I need to change about it.

I am using C#'08

Thanks in advance and Happy Holidays

What I have done is:

project1 the dll

1. Create an interface to interact with the webbrowser control

public interface IMyBrowser
{
void StartUp();
}

2. Create a custom usercontrol that hosts the webbrowser control that
implements the interface

public class MyBrowser: UserControl, IMyBrowser
{
public void StartUp()
{
this.browser.Navigate(@MyWebsite);
}
}

Project2 the executable

1. Added a reference to the dll
2. Created a mdi form to host the mdi children
void MenuItemClick(...)
{
frmBrowser f = new frmBrowser();
f.mdiParent = this;
f.Show();
}

3. Created a mdi child form to host the browser control from the dll
public void frmBrowser()
{
InitializeComponent();
MyDll.MyBrowser instance =
(MyDll.MyBrowser)GetInstance<MyDll.IMyBrowser>();
if(instance != null)
{
this.controls.add(instance);
instance.dock = DockStyle.Fill;
instance.StartUp();
}
}

private object GetInstance<T>()
{
object instance = null;

string[] files =
Directory.GetFiles(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"*.dll");

foreach (string file in files)
{
Trace.WriteLine("Loading " + file);
try
{
Assembly assembly = Assembly.LoadFile(file);
foreach (Type type in assembly.GetTypes())
{
if (!type.IsClass || type.IsNotPublic) continue;
Type[] interfaces = type.GetInterfaces();
if (((IList)interfaces).Contains(typeof(T)))
{
instance = Activator.CreateInstance(type);
break;
}
}
}
catch (Exception ex)
{
//throw;
}
}
return instance;
}

A couple of questions:

1. If creating these browsers in a new thread is the goal, then where is
your code to start them in a new thread?

2. Why the complicated reflection code to create the instance? If two
classes in the dll implement the interface, there is no guarantee which
class you will get.
 
amdrit said:
I suppose this is where I am lost. MDI Children have to be on the same
thread as the MDI Parent as far as I can tell. I was hoping to be able to
create an instance of my usercontrol in seperate appdomains as a work
around. [...]

An AppDomain is for separating data, not threads.

As far as creating different WebBrowser instances on different threads,
that's fairly easy. You just need to create a new Thread instance with
an appropriate entry point, call
Thread.SetApartmentState(ApartmentState.STA), and then start the thread.

As far as what constitutes "an appropriate entry point", you need a
method that does two things: creates your WebBrowser control and any
associated UI (e.g. a Form to contain it…this may be a fully designed
Form you create using the Designer, including a WebBrowser already in
it), and then calls Application.Run() with your desired arguments (the
most likely being simply to pass to the method the instance of the Form
you created to contain the WebBrowser control).

The apartment state needs to be set, because (to oversimplify a bit)
that's what Forms controls require. Creating the new controls in the
correct thread is important, because every control has a thread that
owns it, and ownership is determined when the control is created
(actually, when the handle is created, but if you always do _everything_
on the thread you want to own the control, you never have to worry about
accidentally creating the handle in the wrong thread).

Calling Application.Run() is just as important, because your thread's
controls won't get any of their window messages without a message loop,
and in Forms, it's the Application.Run() method that implements this
message loop. Without the message loop, you'll get just a little bit of
your window showing up, and then it won't do anything else.

Note that you may have very little success trying to mix controls from
different threads in the same control hierarchy. Theoretically, I think
there's probably a way to get it to work, but the Forms namespace
implementation is going to assume that controls related to each other
are owned by the same thread, and there may be no practical way around
that. I don't know what you mean "it will not work fine in tabbed
pages", but if you are trying to get WebBrowser instances that are owned
by different threads to be displayed in the same TabControl, on
different TabPages, that may or may not work well, even if you do
everything correctly to create the instances in different threads.

Pete
 
Back
Top