Adding a control from a different AppDomain... how?

T

ThunderMusic

Hi,
I currently have an application that must load plugins. It's impossible to
unload an assembly inside a single AppDomain, so I must create a different
AppDomain for each plugin I load so I can uload it when I don't need it
anymore. In the design document I received, I have to create the following :
"Load the assembly, get the configuration user control, display the user
control... When the user control is not needed anymore, unload the
assembly."... I can load the assembly (in a different appdomain), I can get
the user control, I can unload the assembly (by unloading the appdomain),
but I can't display the user control because I receive an exception (which I
don't really understand) : "Remoting cannot find field 'parent' on type
'System.Windows.Forms.Control'."... How can it not find the field 'parent'?
Is there a way to make it work? I can't believe MS didn't think about this
possibility which is used in about all "plugin based" application, so there
must be something I'm missing.

Thanks

ThunderMusic
 
N

Nicholas Paldino [.NET/C# MVP]

The solution that you have right now is to use the System.AddIn
namespace, and use a WPF control from another app domain (this scenario is
supported). The WPF control would then have Windows Forms controls embedded
in it (through Windows Forms <-> WPF interop) and you would embed that
control on your form. Here is the post from the CLR Add-In Team Blog which
explains the process:

http://blogs.msdn.com/clraddins/arc...t-for-windows-forms-in-hosts-and-add-ins.aspx
 
T

ThunderMusic

hi, thanks for your answer... but it would require framework 3.5 and we
have to use 2.0 right now because the application has been created some time
ago and we only have VS 2005...

Thanks

ThunderMusic

Nicholas Paldino said:
The solution that you have right now is to use the System.AddIn
namespace, and use a WPF control from another app domain (this scenario is
supported). The WPF control would then have Windows Forms controls
embedded in it (through Windows Forms <-> WPF interop) and you would embed
that control on your form. Here is the post from the CLR Add-In Team Blog
which explains the process:

http://blogs.msdn.com/clraddins/arc...t-for-windows-forms-in-hosts-and-add-ins.aspx


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

ThunderMusic said:
Hi,
I currently have an application that must load plugins. It's impossible
to unload an assembly inside a single AppDomain, so I must create a
different AppDomain for each plugin I load so I can uload it when I don't
need it anymore. In the design document I received, I have to create the
following : "Load the assembly, get the configuration user control,
display the user control... When the user control is not needed anymore,
unload the assembly."... I can load the assembly (in a different
appdomain), I can get the user control, I can unload the assembly (by
unloading the appdomain), but I can't display the user control because I
receive an exception (which I don't really understand) : "Remoting cannot
find field 'parent' on type 'System.Windows.Forms.Control'."... How can
it not find the field 'parent'? Is there a way to make it work? I can't
believe MS didn't think about this possibility which is used in about all
"plugin based" application, so there must be something I'm missing.

Thanks

ThunderMusic
 
T

ThunderMusic

Hi, thanks for your answer....

you say : "Never create a control on a thread or in an appdomain other than
the one which it will essentially be running in."...
How can I create a control directly from my application's AppDomain if I
want to load it from the assembly and unload (or dispose) the assembly
afterward? To do this, I must create it in a different AppDomain (if I
understand it all right). Actually, all we need to do is done and working,
except this point... If we load the assembly from the main AppDomain,
everything works great, but we need to unload the assemblies so we don't
take like 1gb of memory... ;) the plugins can be pretty big (like 10-12Mb
each) and there can be many (up to int.Max) so we don't want our users to
have to restart the application because they loaded up too many plugins in
the same session just because we can't unload them...

Take for instance, a product that is well known (not MS tought), Cakewalk
Sonar, or Digidesign Protools (audio products)... They use VST plugins (and
many others types)... each plugin has it's own UI for configuration...
That's exactly what we want to do... Can you imagine such products if they
didn't unload the plugins they used in the session? the memory would
increase and increase so nothing else can be done... it would be awful...

Well anyway... I use different AppDomain to load the assemblies, get the
Usercontrols from these appdomains and keep the appdomain alive during all
my use of the created usercontrol, and unload the appdomain when the
usercontrol is not needed anymore and the process is done... If it is in
anyway possible to unload an assembly from the main appdomain or to use a
user control from a second appdomain, I need to know how... ;)

Thanks a lot

ThunderMusic
 
L

london calling

Hi ThunderMusic.. No real answers but maybe some things to think about.. Does
the UI and the actual bones of the processing (I'm assuming audio FX) have to
be in the same assembly? If not the UI can be part of the same App domain and
just create 'unloadable' appdomains to host the processors as required.. Also
are the assemblies so big because they contain lots of embedded resources? if
so could they be moved to disk and forget unloading the assembly? just a
thought.. jd
 
J

Jon Skeet [C# MVP]

ThunderMusic said:
Hi, thanks for your answer....

you say : "Never create a control on a thread or in an appdomain other than
the one which it will essentially be running in."...
How can I create a control directly from my application's AppDomain if I
want to load it from the assembly and unload (or dispose) the assembly
afterward?

You need to load one of your assemblies into the new AppDomain, and get
that to create the control.
To do this, I must create it in a different AppDomain (if I
understand it all right). Actually, all we need to do is done and working,
except this point... If we load the assembly from the main AppDomain,
everything works great, but we need to unload the assemblies so we don't
take like 1gb of memory... ;) the plugins can be pretty big (like 10-12Mb
each) and there can be many (up to int.Max) so we don't want our users to
have to restart the application because they loaded up too many plugins in
the same session just because we can't unload them...

Indeed. That's one of the reasons we do it too.
Take for instance, a product that is well known (not MS tought), Cakewalk
Sonar, or Digidesign Protools (audio products)... They use VST plugins (and
many others types)... each plugin has it's own UI for configuration...
That's exactly what we want to do... Can you imagine such products if they
didn't unload the plugins they used in the session? the memory would
increase and increase so nothing else can be done... it would be awful...

Well anyway... I use different AppDomain to load the assemblies, get the
Usercontrols from these appdomains and keep the appdomain alive during all
my use of the created usercontrol, and unload the appdomain when the
usercontrol is not needed anymore and the process is done... If it is in
anyway possible to unload an assembly from the main appdomain or to use a
user control from a second appdomain, I need to know how... ;)

No, you can't unload a single assembly from an AppDomain, you have to
unload the whole AppDomain. The important bit is to get some of your
own code (just enough) running in the new AppDomain, so that you can
control it from your "main" AppDomain but still create appropriate
objects (most importantly UI controls) inside the new AppDomain, in the
right thread.
 
L

london calling

Maybe more helpful.. check out the System.AddIn namespace/subspaces.. HTH jd
 
T

ThunderMusic

Hi,
It actually not audio FX... The current architecture is that we have a
Windows service running which is loading the assemblies to run some
monitoring modules. There's also a "client" application which connects to
the Windows service and asks for a list of the loaded modules and displays
them in a treeview (for design purposes). When the user clicks on the node
of a module, it can see it's configuration control appear in the panel
beside the treeview. Well, that's actually what it should do... Passing a
control via remoting alone doesn't work at all... Actually, the control
goes though, but I can't add it to the panel saying it can't call a static
method remotly... anyways... so we decided to send the library itself (in a
byte[]) via remoting and the client app loads it dynamicly to get the
control and then binds it to the proxy to the remote class in the service to
the module it must be bound to. Right now, everything works fine if we load
the assembly locally in the main AppDomain, but it seems that if remoting is
involved at all, nothing can be done for user controls even if it's
"transparent"...

So we'll try to find something to solve our problem...

Thanks

ThunderMusic
 
T

ThunderMusic

thanks a lot for your help... ;)

Jon Skeet said:
You need to load one of your assemblies into the new AppDomain, and get
that to create the control.


Indeed. That's one of the reasons we do it too.


No, you can't unload a single assembly from an AppDomain, you have to
unload the whole AppDomain. The important bit is to get some of your
own code (just enough) running in the new AppDomain, so that you can
control it from your "main" AppDomain but still create appropriate
objects (most importantly UI controls) inside the new AppDomain, in the
right thread.
 
B

Bill Woodruff

Visual Studio 2005, .NET FrameWork 2.0, C#, WinForms Application

Hi,

I've read the recent posts by and to 'Thunderbird' (and learned a lot,
thanks, from the usual masters Skeet and Paladino, and others) which involve
AppDomains in a remoting scenario, but I think the issue I am working with
.... while related ... is sufficiently different to warrant a new thread.

I'm also working on a plug-in architecture it's for a WinForms based
project.

At run-time I "discover" and load all the Plug-Ins that match a MainApp
Interface with no problem.

I find I can pass complex objects (like the TreeNode of a TreeView) to the
plug-ins' AppDomain, and modify certain properties and have the result show
up in the Main App's UI, no problem.

What I find I can't do ... I assume because WinForm objects like a TreeView
and the Nodes collection object of a TreeView are passed as proxies ... is
create new Nodes in the plug-in app domain and then pass them back to the
Main app and put them in the TreeView in the UI there.

Error trying to create a new TreeView Node in the Plug-In appDomain (yes,
Plug-Ins do contain references for WinForms) :

"SerializationException Crossed an AppDomain Boundary"

Type 'System.Windows.Forms.Control+ControlCollection' in assembly
'System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089' is not marked as serializable.

"Ideally" I'd like to pass the Plug-In a bounding rectangle definition, have
it create a collection of controls, and get those controls back to the Main
AppDomain and plug them into a Panel and then have the Plug-In, based on the
state of its controls, do the "right thing" to conjure up whatever objects
it wants to create and get those objects back to the Main App for
display/use in the main UI.

At this point I believe that is simply not possible ... although Jon Skeet's
comments in his reply to Thunderbird are still inviting me to reconsider and
re-think.

What I do believe is most probable is that there is an optimum architecture
for a situation where :

1. you want to load plug-ins in a separate appDomain (in T'Bird's case one
appDomain per plug-in). so you can unload at will.

2. you want the plug-in to specify its run-time user interface to the Main
appDomain in some terse, easily parsable form.

3. you want the Main appDomain to present the Plug-In's controls/widgets in
the main UI, let the user alter their state at run-time.

4. you want the Plug-In (through direct action at run-time) to trigger some
process that results in modification of complex objects, or creation of new
ones in the Main appDomain.

Appreciate any feedback. I have examined the various add-in/plug-in projects
of CodeProject, the usual books (Petzold, Sells, Liberty, Gunnerson).

It would help me greatly if I had a better sense of exactly what a "proxy"
is, and I appreciate any referrals to learning materials in that area.

thanks !

Bill Woodruff
dotScience
Chiang Mai, Thailand
 
J

Jon Skeet [C# MVP]

At this point I believe that is simply not possible ... although Jon Skeet's
comments in his reply to Thunderbird are still inviting me to reconsider and
re-think.

Well, it sounds like we're doing significantly different things. In my
case I was able to have a whole area of the main UI which the plugin
"owned", running that UI in a different thread.
What I do believe is most probable is that there is an optimum architecture
for a situation where :

1. you want to load plug-ins in a separate appDomain (in T'Bird's case one
appDomain per plug-in). so you can unload at will.

2. you want the plug-in to specify its run-time user interface to the Main
appDomain in some terse, easily parsable form.

3. you want the Main appDomain to present the Plug-In's controls/widgets in
the main UI, let the user alter their state at run-time.

4. you want the Plug-In (through direct action at run-time) to trigger some
process that results in modification of complex objects, or creation of new
ones in the Main appDomain.

Right, that sounds like a reasonable architecture, yes. Easier with WPF
of course, because of the business of specifying the user interface is
easy :)
It would help me greatly if I had a better sense of exactly what a "proxy"
is, and I appreciate any referrals to learning materials in that area.

Yes - AppDomains and marshalling is a fairly difficult topic to find
information about. Basically anything which derives from
MarshalByRefObject can be marshalled across an AppDomain boundary by
(automatically) creating a proxy which has a reference to the original
object. However, there are limitations as to what you can do, and I'm
afraid I'm far from an expert on that side of things :(

I basically try to do as little as possible across the app domain
boundary.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top