Ik ben bezig met het schrijven van een applicatie die uitbreidbaar moet zijn dmv een soort plug-in structuur.
Nu is het in .NET mogelijk om dynamisch assemblies te laden uit (bijvoorbeeld) een subdirectory.
Ik heb het nu dus al zo ver dat als ik een DLL in die plugin directory kopieer, dat deze automatisch door mijn applicatie ingelezen en gebruikt wordt.
Maar ik wil het ook zo maken dat als ik een plugin verwijder of een nieuwere versie plaats, dat deze ook uit uit mijn programma geknikkerd wordt, danwel bijgewerkt naar de nieuwste versie.
Helaas biedt .NET niet de mogelijkheid om een eenmaal geladen assembly weer uit het geheugen te krijgen.
Na enig zoekwerk kwam ik op de MSDN site het volgende artikel tegen 'AppDomains and Dynamic Loading', waarin een oplossing voor dit probleem aangedragen wordt.
Het is namelijk mogelijk om een nieuw AppDomain te definieren, waarin je je plugin assemblies kunt laden. Bij een plugin wijziging unload je dan het AppDomain en leest alle plugins weer in in een nieuwe AppDomain.
Ik heb dus mijn code aan de hand van dat artikel aangepast, maar het werkt niet.
Dit is het (ingekorte) stukje waarin de nieuwe AppDomain aangemaakt wordt:
En dit is de ReadAssembly klasse:
Het probleem is dus dat bij het inladen van de plugins denkt dat hij in de WebMonitor.exe AppDomain zit, terwijl dit het Workers AppDomain zou moeten zijn.
Dat blijkt ook uit het feit dat ik de klassen uit de plugin assemblies nog steeds kan gebruiken na het unloaden van de Workers AppDomain.
Weet iemand wat ik fout doe?
Nu is het in .NET mogelijk om dynamisch assemblies te laden uit (bijvoorbeeld) een subdirectory.
Ik heb het nu dus al zo ver dat als ik een DLL in die plugin directory kopieer, dat deze automatisch door mijn applicatie ingelezen en gebruikt wordt.
Maar ik wil het ook zo maken dat als ik een plugin verwijder of een nieuwere versie plaats, dat deze ook uit uit mijn programma geknikkerd wordt, danwel bijgewerkt naar de nieuwste versie.
Helaas biedt .NET niet de mogelijkheid om een eenmaal geladen assembly weer uit het geheugen te krijgen.
Na enig zoekwerk kwam ik op de MSDN site het volgende artikel tegen 'AppDomains and Dynamic Loading', waarin een oplossing voor dit probleem aangedragen wordt.
Het is namelijk mogelijk om een nieuw AppDomain te definieren, waarin je je plugin assemblies kunt laden. Bij een plugin wijziging unload je dan het AppDomain en leest alle plugins weer in in een nieuwe AppDomain.
Ik heb dus mijn code aan de hand van dat artikel aangepast, maar het werkt niet.
Dit is het (ingekorte) stukje waarin de nieuwe AppDomain aangemaakt wordt:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| class Monitor { private AppDomainSetup setup; private AppDomain appDomain; public Monitor() { string path=AppDomain.CurrentDomain.BaseDirectory; setup = new AppDomainSetup(); setup.ApplicationBase = path; setup.CachePath= path + "cache\\"; setup.ApplicationName = "Workers"; setup.ShadowCopyFiles = "true"; setup.ShadowCopyDirectories = path; appDomain = null; } public void Start() { ArrayList workers; Worker worker; ReadAssembly workerDomain; // Creeer nieuw AppDomain appDomain = AppDomain.CreateDomain("Workers", null, setup); // Geeft 'Workers' :) Console.WriteLine(appDomain.FriendlyName); // Creeer klasse om plugins in te gaan lezen in nieuwe AppDomain workerDomain = (ReadAssembly) appDomain.CreateInstanceFromAndUnwrap ("WebMonitorTest.exe", "ReadAssembly"); // geeft 'WebMonitorTest.exe' :) Console.WriteLine(AppDomain.CurrentDomain.FriendlyName); // Roep plugin loader functie aan in aangemaakte AppDomain workers = workerDomain.ReadWorkerAssemblies(); // Doe een hoop troep met de plugins // KNIP // Geef AppDomain weer vrij workers=null; workerDomain=null; AppDomain.Unload(appDomain); appDomain = null; } } |
En dit is de ReadAssembly klasse:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| [Serializable] public class ReadAssembly { public ArrayList ReadWorkerAssemblies() { return ReadAssemblies(new Worker().GetType()); } private ArrayList ReadAssemblies(Type assemblyType) { ArrayList types = new ArrayList(); string currentDirectory = System.AppDomain.CurrentDomain.BaseDirectory; string[] dllFilenames = System.IO.Directory.GetFiles(currentDirectory, "*.dll"); // geeft 'WebMonitor.exe' :( Console.WriteLine(System.AppDomain.CurrentDomain.FriendlyName); foreach(string filename in dllFilenames) { try { Assembly asm = Assembly.LoadFrom(filename); Type[] typesInAssembly = asm.GetTypes(); foreach(Type type in typesInAssembly) { if(type.IsSubclassOf(assemblyType)) { types.Add(type); } } } catch(BadImageFormatException e) { // Not a valid assembly, move on e=e; // To Disable 'e is never used' warning } } return types; } } |
Het probleem is dus dat bij het inladen van de plugins denkt dat hij in de WebMonitor.exe AppDomain zit, terwijl dit het Workers AppDomain zou moeten zijn.
Dat blijkt ook uit het feit dat ik de klassen uit de plugin assemblies nog steeds kan gebruiken na het unloaden van de Workers AppDomain.
Weet iemand wat ik fout doe?
No trees were killed in the sending of this message. However a large number of electrons were terribly inconvenienced.