Click to See Complete Forum and Search --> : Loading Assembly With AppDomain


badwolf500
May 1st, 2007, 01:36 PM
Hi,

I am trying to load/unload assemblies (dll's) using AppDomain but I am having some trouble when the assembly is not in the same directory as the application exe. If it is in the same directory as the exe everything is fine but if it is in another one the ad.Load(a) call fails and throws a FileNotFoundException. Does anyone have any ideas of how I can solve this.

Thanks,
badwolf


Assembly MyAssembly;
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationName = "MyApplication";

AppDomain ad = AppDomain.CreateDomain("Test", null, setup);

try
{
AssemblyName a = AssemblyName.GetAssemblyName(FileName);

MyAssembly = ad.Load(a);
}
catch
{
MessageBox.Show("Error", "Invalid Plugin", MessageBoxButtons.OK, MessageBoxIcon.Error);
}

MadHatter
May 1st, 2007, 03:51 PM
to load / unload assemblies you need an assembly manager to interact w/ the objects loaded in the other appdomain. you need to set a few more things in the app domain to point to where your main binary path is.

I'm attaching a sample you can look at (the assembly you can test loading / unloading is the ClassLibaray1 included in the solution).

I haven't looked at it in a while but if you have any questions let me know. I think the AppDomainLoadManager and InteropAsm dll's needs to be gac'd

edit: ok, I looked at it again. this may be a little more full featured that what you were originally asking about. but when you load an assembly into another app domain (so that you can unload the assembly later on) there are a few things you need (which this sample shows how to do).

the first is that you load assemblies into another "app domain" meaning the app domain that you're currently running in doesn't have access to those assemblies. in order to use the plugins you've loaded into the other app domain you need to provide an assembly load manager (which is GAC'd to make the process easier, otherwise it needs to be in both the primary app domain's executable path, and the remote app domain's local path). .NET uses Remoting to do the inter app domain communication, so the load manager needs to be a marshalbyref object. this the first object that you load up inside your app domain which allows you to load additional assemblies, and interact w/ them.

second, they need to share a common interop assembly (the dll that defines the interface your plugins will use) which both the current app domain and the secondary app domain understands. from this point on, its just like remoting. you have a remote object (Class1 in ClassLibrary1) in the secondary app domain, and a remote client version of it in the current app domian. they are able to interact (primary app domain and secondary app domain) through that plugin interface.

hope that helps to shed some light on how to do it...

badwolf500
May 4th, 2007, 04:33 AM
Thanks MadHatter - much appreciated and I'll check that out ASAP.

I should describe what I am trying to do just in case I am going down the wrong route.

The application I am trying to write controls some hardware and to do that I need to load the SDK for the hardware. This is a standard C DLL.
When someone is using the hardware they may wish to load another version of the SDK and use it to control the hardware instead. This is when I found the problem of not being able to unload the first assembly.

Is AppDomain etc. the correct method for me here or do I need something else?

Thanks,
badwolf500

MadHatter
May 4th, 2007, 09:36 AM
nope. to interact with non-.net libraries you have to platform invoke them. you still need a managed assembly to do that, and you would load that managed assembly this way. but that assembly needs to import the functionality of the other dll. to do that you DllImport the functions you need:

public class NativeMethods {

[DllImport("some.dll" /* may need to specify more info here */ )]
public static extern void SomeFunctionName();

}

badwolf500
May 8th, 2007, 04:37 AM
Thanks again MadHatter - I don't think I have fully explained my application.

I am writing a C# application which needs to load C# assemblies (dlls) and C dlls. there may be different versions of the C# and C dlls and the user will want the option of selecting these without needing to shut the application down.

To load my C dlls I have written a wrapper like the one you described -

public class NativeMethods {

[DllImport("some.dll" /* may need to specify more info here */ )]
public static extern void SomeFunctionName();

}

but I need one for each C dll I have since I don't think it is possible to change the "some.dll" path dynamically. For this reason I need to be able to load my C# wrapper and then unload it if the user wishes to do so.

For my C# assemblies I need to do the same thing - the user may choose to use a different version and I will need to unload the old one and load a new one.

I know this all sounds very strange but it is test software for hardware devices and there will be a lot of rolling back to older versions to test older pieces of equipment.

Is AppDomain the only way I can successfully load and unload the C# assemblies and C dlls?

Thanks again,
badwolf500

darwen
May 8th, 2007, 04:42 AM
Personally I'd just P/Invoke directly into Win32 to do this (LoadLibrary, GetProcAddress etc etc) : or have a seperate C++ dll which is capable of switching between versions which you then P/Invoke into.

In actual fact on further reflection having a seperate C++ dll might be the best option.

I try to keep things as simple as possible, and this does appear to be the most straightforward solution to me.

Darwen.

badwolf500
May 8th, 2007, 04:59 AM
Personally I'd just P/Invoke directly into Win32 to do this (LoadLibrary, GetProcAddress etc etc) : or have a seperate C++ dll which is capable of switching between versions which you then P/Invoke into.

In actual fact on further reflection having a seperate C++ dll might be the best option.

I try to keep things as simple as possible, and this does appear to be the most straightforward solution to me.

Darwen.

Thanks Darwen - that will work for my C dlls but for my C# assemblies (which are also dlls) I still need to be able to load/unload during the applications lifetime. Does AppDomain sound like the best (and perhaps only) way to do this or am I missing something?

darwen
May 8th, 2007, 06:37 AM
Yes, using AppDomains is the only way of dynamically loading and unloading assemblies. But why would you want to do that ?


The application I am trying to write controls some hardware and to do that I need to load the SDK for the hardware. This is a standard C DLL.
When someone is using the hardware they may wish to load another version of the SDK and use it to control the hardware instead.


This is unusual behaviour to say the least. I've never heard of an instance of changing SDKs whilst an application is in operation. You usually fire up the application with whichever version of the SDK you require at that time.

But you shouldn't be writing new code (i.e. new assemblies) which are designed to be dynamically loaded and unloaded if you don't really, really need to. There are all sorts of other methods you can use to get the same result e.g. using an assembly (dll) containing interfaces, and then having seperate assemblies (dlls) which implement the interfaces.

Having multiple AppDomains in your application seems to me to be added a huge amount of complexity when you can acheive your goal much more simply using other methods.

Darwen.

badwolf500
May 8th, 2007, 07:26 AM
Hi Darwen,

Thanks for your help but I don't think you are getting my point and perhaps I am not explaining it properly

The application is supposed to run for weeks at a time without ever being closed. During this time one piece of hardware it is connected to may be upgraded. If this happens I would like to be able to replace the C SDK DLL for this hardware without shutting down the entire application.

Also other pure C# assemblies may be updated (these have nothing to do with hardware). I would also like to replace these.

I don't think his behaviour is that uncommon - you say there are other options but I don't know what they are.

Thanks,
badwolf500

darwen
May 8th, 2007, 09:53 AM
you say there are other options but I don't know what they are.


I've already given you an example :


e.g. using an assembly (dll) containing interfaces, and then having seperate assemblies (dlls) which implement the interfaces.


Let me elaborate on this.

You have 2 .NET assemblies/dlls : one containing just interfaces and the other implementations.


// in dll 1 Interfaces.dll
public interface ITest
{
void Test();
}

// in dll 2 Implementation1.dll, which references Interfaces.dll
public class TestImpl : public ITest
{
public override void Test()
{
System.Diagnostics.Debug.WriteLine("Test1");
}
}

// in a different dll, Implementation2.dll, which again references Interfaces.dll
public class TestImpl : public ITest
{
public override void Test()
{
System.Diagnostics.Debug.WriteLine("Test1Version2");
}
}


Now, if we read Implementation1.dll dynamically and add Interfaces.dll to a project we can do this :


public static class TestLoader
{
static public ITest GetInstanceOfTest(string assemblyFileName)
{
Assembly assembly = Assembly.LoadFile(assemblyFileName);
return Activator.CreateInstance(assembly.GetName(), "TestImpl") as ITest;
}
}


Therefore in the main code we can do


ITest test = TestLoader.GetInstanceOfTest("Implementation1.dll");
test.Test();

ITest test2 = TestLoader.GetInstanceOfTest("Implementation2.dll");
test2.Test();


Now, the fully qualified name of TestImpl will not be the same between the two dlls, but you can derive its fully qualified name from the name of the assembly. Or alternatively (my preferred route ) you can search the implementation's types using reflection to find all types which implement ITest. It's up to you.

The point is this method doesn't require AppDomains, dynamic unloading of assemblies etc.

A point to note is that this is exactly the same as how you would go about writing a plug-in architecture in .NET.

For the calls to the C dll like I've already said you can have a C++ static dll which accepts the SDK dll's path and LoadLibrary it inside. Again no AppDomains necessary.

I hope you get what I mean here.

Darwen.

badwolf500
May 14th, 2007, 07:28 AM
Hi Darwen,

Thank you very much for taking the time to post your replies and example code. This worked well for me.

Best Regards,
badwolf500

TheCPUWizard
May 14th, 2007, 07:37 AM
Just remember if you import (i.e. USE) a single ctype from the assembly loaded into a secondary domain from another (e.g. Primary) domain, you will be unable to unload that assembly..

badwolf500
May 16th, 2007, 06:55 AM
Hi TheCPUWizard,

Could you clarify that for me or show a simple example building on Darwen's code? I don't have very much C# experience as you can probably tell by my questions and I do really appreciate this help.

Thanks,
badwolf500

badwolf500
May 18th, 2007, 05:54 PM
Just remember if you import (i.e. USE) a single ctype from the assembly loaded into a secondary domain from another (e.g. Primary) domain, you will be unable to unload that assembly..

I know I have got a lot of help here but it is important for my application that I have this right. Does this mean if create an instance of a class (or any type) in the assembly that I will not be able to unload it? - or something else.

Thanks,
badwolf500

TheCPUWizard
May 18th, 2007, 09:51 PM
Badwolf500,

Sorry, I have been swamped, and hoped someone else would pick up on this thread...

Here is a pretty good link that covers the majority of the "gotcha's"

http://www.devsource.com/article2/0,1759,1790388,00.asp

badwolf500
May 22nd, 2007, 04:01 AM
Thanks for the link and your help CPUWizard - much appreciated.

badwolf500