source: trunk/CrypCore/PluginManager.cs @ 2058

Last change on this file since 2058 was 2058, checked in by Matthäus Wander, 11 years ago

CrypCore:

  • fixed assembly loading routine
File size: 14.9 KB
Line 
1/*
2   Copyright 2008 Martin Saternus, University of Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.Reflection;
20using System.IO;
21
22namespace Cryptool.Core
23{
24    /// <summary>
25    /// PluginManager Class 
26    /// </summary>
27    public class PluginManager
28    {
29        /// <summary>
30        /// Counter for the dll files that were found
31        /// </summary>
32        private int availablePluginsApproximation = 0;
33
34        /// <summary>
35        /// Subdirectory to store plugins
36        /// </summary>
37        private const string PluginDirecory = "CrypPlugins";
38
39        /// <summary>
40        /// Fires if an exception occurs
41        /// </summary>
42        public event CrypCoreExceptionEventHandler OnExceptionOccured;
43
44        /// <summary>
45        /// Fires if an info occurs
46        /// </summary>
47        public event CrypCoreDebugEventHandler OnDebugMessageOccured;
48
49        /// <summary>
50        /// Occurs when a plugin was loaded
51        /// </summary>
52        public event CrypCorePluginLoadedHandler OnPluginLoaded;
53       
54        /// <summary>
55        /// Custom Plugin Store Directory
56        /// </summary>
57        private readonly string customPluginStore;
58
59        /// <summary>
60        /// Global Plugin Store Directory
61        /// </summary>
62        private readonly string globalPluginStore;
63
64        /// <summary>
65        /// Loaded Assemblies
66        /// </summary>
67        private readonly Dictionary<string, Assembly> loadedAssemblies;
68
69        /// <summary>
70        /// Loaded Types
71        /// </summary>
72        private readonly Dictionary<string, Type> loadedTypes;
73
74        Dictionary<string, Assembly> foundAssemblies = new Dictionary<string, Assembly>();
75       
76        /// <summary>
77        /// cTor
78        /// </summary>
79        public PluginManager() 
80        { 
81            this.customPluginStore = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), PluginDirecory);
82            this.globalPluginStore = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), PluginDirecory);
83            this.loadedAssemblies = new Dictionary<string, Assembly>();
84            this.loadedTypes = new Dictionary<string, Type>();         
85        }
86
87        public PluginManager(string path)
88        {
89          this.customPluginStore = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), PluginDirecory);
90          this.globalPluginStore = path;
91          this.loadedAssemblies = new Dictionary<string, Assembly>();
92          this.loadedTypes = new Dictionary<string, Type>();
93        }
94
95        /// <summary>
96        /// Returns a type of a loaded assembly
97        /// </summary>
98        /// <param name="assemblyName">Assembly Name</param>
99        /// <param name="typeName">Type Name</param>
100        /// <returns>Return the type or null if no type could be found</returns>
101        public Type LoadType(string assemblyName, string typeName)
102        {
103            if (this.loadedAssemblies.ContainsKey(assemblyName))
104                return this.loadedAssemblies[assemblyName].GetType(typeName, false);
105            return null;
106        }
107
108        /// <summary>
109        /// Returns all types found in the plugins
110        /// </summary>
111        /// <param name="state">Load type from all plugins or from signed only</param>
112        /// <returns></returns>
113        public Dictionary<string, Type> LoadTypes(AssemblySigningRequirement state)
114        {
115            if (Directory.Exists(globalPluginStore))
116            {
117              availablePluginsApproximation = AvailablePluginsApproximation(new DirectoryInfo(globalPluginStore));
118              FindAssemblies(new DirectoryInfo(globalPluginStore), state, foundAssemblies);
119            }
120
121            // custom plugin store is not supported yet
122            //if (!Directory.Exists(customPluginStore))
123            //    Directory.CreateDirectory(customPluginStore);
124            //FindAssemblies(new DirectoryInfo(customPluginStore), state, foundAssemblies);
125            LoadTypes(foundAssemblies);
126            return this.loadedTypes;
127        }
128
129
130        [Obsolete("will be removed soon")]
131        private int AvailablePluginsApproximation(DirectoryInfo directory)
132        {
133          int count = 0;
134          foreach (DirectoryInfo subDirectory in directory.GetDirectories())
135          {
136            count = AvailablePluginsApproximation(subDirectory);
137          }
138          return directory.GetFiles("*.dll").Length;
139        }
140
141        /// <summary>
142        /// Search all subdirectories for assemblies
143        /// </summary>
144        /// <param name="directory">Root directory</param>
145        /// <param name="state">Search for all or only for signed assemblies</param>
146        /// <param name="foundAssemblies">list of found assemblies</param>
147        private void FindAssemblies(DirectoryInfo directory, AssemblySigningRequirement state, Dictionary<string, Assembly> foundAssemblies)
148        {
149            foreach (DirectoryInfo subDirectory in directory.GetDirectories())
150            {
151                FindAssemblies(subDirectory, state, foundAssemblies);
152            }
153
154            int currentPosition = 0;
155            foreach (FileInfo fileInfo in directory.GetFiles("*.dll"))
156            {
157                currentPosition++;
158                try
159                {
160                    Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(fileInfo.FullName));
161                   
162                    string key = GetAssemblyKey(asm.FullName, state);
163                    if (key == null)
164                        throw new UnknownFileFormatException(fileInfo.FullName);
165
166                    bool sendMessage = false;
167                    if (!foundAssemblies.ContainsKey(key))
168                    {
169                      foundAssemblies.Add(key, asm);
170                      sendMessage = true;
171                    }
172                    else
173                      if (new AssemblyName(asm.FullName).Version > new AssemblyName(foundAssemblies[key].FullName).Version)
174                      {
175                        foundAssemblies[key] = asm;
176                        sendMessage = true;
177                      }
178
179                    if (sendMessage)
180                    {
181                        SendDebugMessage("Loaded Assembly \"" + asm.FullName + "\" from file: " + fileInfo.FullName);
182                        if (OnPluginLoaded != null)
183                        {
184                          OnPluginLoaded(this, new PluginLoadedEventArgs(currentPosition, this.availablePluginsApproximation, asm.GetName().Name + " Version=" + asm.GetName().Version.ToString()));
185                        }                         
186                    }
187                }
188                catch (BadImageFormatException)
189                {
190                  SendExceptionMessage(string.Format(Resources.Exceptions.non_plugin_file, fileInfo.Name));
191                }
192                catch (Exception ex)
193                {
194                    SendExceptionMessage(ex);
195                }
196            }
197        }
198
199        /// <summary>
200        /// Interate the found assemblies and add well known types
201        /// </summary>
202        /// <param name="foundAssemblies">list of found assemblies</param>
203        private void LoadTypes(Dictionary<string, Assembly> foundAssemblies)
204        {
205            string interfaceName = "Cryptool.PluginBase.IPlugin";
206
207            foreach (Assembly asm in foundAssemblies.Values)
208            {
209                AssemblyName assemblyName = new AssemblyName(asm.FullName);
210                try
211                {
212                  foreach (Type type in asm.GetTypes())
213                  {
214                    if (type.GetInterface(interfaceName) != null && !this.loadedTypes.ContainsKey(type.FullName))
215                    {
216                      this.loadedTypes.Add(type.FullName, type);
217                      if (!this.loadedAssemblies.ContainsKey(assemblyName.Name))
218                      {
219                        this.loadedAssemblies.Add(assemblyName.Name, asm);
220                      }
221                    }
222                  }
223                }
224                catch (ReflectionTypeLoadException tle)
225                {
226                  if (OnExceptionOccured != null)
227                  {
228                    OnExceptionOccured(this, new PluginManagerEventArgs(new TypeLoadException(asm.FullName + "\n" + tle.LoaderExceptions[0].Message)));
229                  }
230                }
231                catch (Exception exception)
232                {
233                  if (OnExceptionOccured != null)
234                  {
235                    OnExceptionOccured(this, new PluginManagerEventArgs(new TypeLoadException(asm.FullName + "\n" + exception.Message)));
236                  }
237                }
238            }
239        }
240
241        /// <summary>
242       /// Create a unique key for each assembly
243       /// </summary>
244       /// <param name="assemblyFullName">Full name of the assembly</param>
245       /// <param name="state">Signed or unsigned</param>
246       /// <returns>Returns the key or null if public key is null and signing is required</returns>
247        private string GetAssemblyKey(string assemblyFullName, AssemblySigningRequirement state)
248        {
249            AssemblyName asmName = new AssemblyName(assemblyFullName);
250            if (state == AssemblySigningRequirement.LoadSignedAssemblies)
251            {
252                if (asmName.KeyPair.PublicKey == null)
253                    return null;
254                return asmName.Name + "__" + asmName.KeyPair.ToString();
255            }
256            return asmName.Name;
257        }
258
259        /// <summary>
260        /// Adds an assembly to the store
261        /// </summary>
262        /// <param name="buffer">byte[] of the assembly</param>
263        /// <param name="state">Signed or unsigned</param>
264        /// <param name="pluginStore">Global or Custom Store</param>
265        public void AddPlugin(byte[] buffer, AssemblySigningRequirement state, PluginStore pluginStore)
266        {
267            try
268            {
269                Assembly asm = Assembly.ReflectionOnlyLoad(buffer);
270                AssemblyName asmName = new AssemblyName(asm.FullName);
271
272                string publicKeyToken = GetPublicToken(asm);
273                if ((state == AssemblySigningRequirement.StoreSignedAssemblies) && (publicKeyToken == string.Empty))
274                    throw new AssemblyNotSignedException();
275
276                string pluginStoreDirectory = GetPluginDirectory(asmName, publicKeyToken, pluginStore);
277                if (!Directory.Exists(pluginStoreDirectory))
278                    Directory.CreateDirectory(pluginStoreDirectory);
279
280                WriteFile(buffer, pluginStoreDirectory, asmName);
281            }
282            catch (AssemblyNotSignedException ex)
283            {
284                if (OnExceptionOccured != null)
285                    OnExceptionOccured(this, new PluginManagerEventArgs(ex));
286            }
287            catch
288            {
289                if (OnExceptionOccured != null)
290                    OnExceptionOccured(this, new PluginManagerEventArgs(new StoreAddingException()));
291            }
292        }
293
294        /// <summary>
295        /// Writes the buffer to disk
296        /// </summary>
297        /// <param name="buffer">byte[] of the assembly</param>
298        /// <param name="pluginStoreDirectory">Directory to store the assembly</param>
299        /// <param name="assemblyName">Name of the assembly</param>
300        private void WriteFile(byte[] buffer, string pluginStoreDirectory, AssemblyName assemblyName)
301        {
302            string assemblyFileName = Path.Combine(pluginStoreDirectory, assemblyName.Name + ".dll");
303            if (!File.Exists(assemblyFileName))
304            {
305                FileStream fs = File.Create(assemblyFileName);
306                BinaryWriter bw = new BinaryWriter(fs);
307                bw.Write(buffer);
308                bw.Close();
309                fs.Close();
310            }
311        }
312
313        /// <summary>
314        /// Get the directory for the assembly
315        /// </summary>
316        /// <param name="assemblyName">Name of the assembly</param>
317        /// <param name="publicKeyToken">Public token</param>
318        /// <param name="pluginStore">Global or Custom Store</param>
319        /// <returns>Returns the full directory name</returns>
320        private string GetPluginDirectory(AssemblyName assemblyName, string publicKeyToken, PluginStore pluginStore)
321        {
322            string versionName = string.Empty;
323            if (publicKeyToken == String.Empty)
324                versionName = Path.Combine(assemblyName.Name, assemblyName.Version.ToString());
325            else
326                versionName = Path.Combine(assemblyName.Name, assemblyName.Version.ToString() + "__" + publicKeyToken);
327
328            switch (pluginStore)
329            {
330                case PluginStore.GlobalPluginStore:
331                    return Path.Combine(globalPluginStore, versionName);
332                case PluginStore.CustomPluginStore:
333                    return Path.Combine(customPluginStore, versionName);
334            }
335            return String.Empty;
336        }
337
338        /// <summary>
339        /// Returns the public token
340        /// </summary>
341        /// <param name="asm"></param>
342        /// <returns></returns>
343        private string GetPublicToken(Assembly asm)
344        {
345            byte[] hexBytes = asm.GetName().GetPublicKeyToken();
346            string hexString = String.Empty;
347            for (int i = 0; i < hexBytes.Length; i++)
348            {
349                hexString += hexBytes[i].ToString("x");
350            }
351            return hexString;
352        }
353
354        /// <summary>
355        /// Sends a debug message.
356        /// </summary>
357        /// <param name="message">The message.</param>
358        private void SendDebugMessage(string message)
359        {
360          if (OnDebugMessageOccured != null)
361            OnDebugMessageOccured(this, new PluginManagerEventArgs(message));
362        }
363
364        private void SendExceptionMessage(Exception ex)
365        {
366          if (OnExceptionOccured != null)
367            OnExceptionOccured(this, new PluginManagerEventArgs(ex));
368        }
369
370        private void SendExceptionMessage(string message)
371        {
372          if (OnExceptionOccured != null)
373            OnExceptionOccured(this, new PluginManagerEventArgs(message));
374        }
375    }
376}
Note: See TracBrowser for help on using the repository browser.