Changeset 2291


Ignore:
Timestamp:
1/4/2011 5:38:18 AM (4 years ago)
Author:
lowjoel
Message:

Split the LoadingPolicy? attribute, PluginInstance? class and IPlugin interface from Host.cs

Location:
branches/eraser6/pluginsRewrite
Files:
4 added
1 edited
1 moved

Legend:

Unmodified
Added
Removed
  • branches/eraser6/pluginsRewrite/Eraser.Manager/ManagerLibrary.cs

    r2288 r2291  
    5050            ErasureTargetRegistrar = new ErasureTargetRegistrar(); 
    5151            FileSystemRegistrar = new FileSystemRegistrar(); 
     52            Host.Initialise(); 
     53            Host.Instance.Load(); 
    5254        } 
    5355 
  • branches/eraser6/pluginsRewrite/Eraser.Plugins/Host.cs

    r2288 r2291  
    6161 
    6262        /// <summary> 
     63        /// Initialises the Plugins library. Call <see cref="Host.Load"/> when object 
     64        /// initialisation is complete. 
     65        /// </summary> 
     66        /// <remarks>Call <see cref="Host.Instance.Dispose"/> when exiting.</remarks> 
     67        public static void Initialise() 
     68        { 
     69            new DefaultHost(); 
     70        } 
     71 
     72        /// <summary> 
    6373        /// Constructor. Sets the global Plugin Host instance. 
    6474        /// </summary> 
     
    132142    { 
    133143        /// <summary> 
    134         /// Constructor. Loads all plugins in the Plugins folder. 
     144        /// Constructor. 
    135145        /// </summary> 
    136146        public DefaultHost() : base() 
    137         { 
    138             Load(); 
    139         } 
    140  
    141         public override void Load() 
    142147        { 
    143148            //Specify additional places to load assemblies from 
    144149            AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; 
    145150            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ResolveReflectionDependency; 
    146  
     151        } 
     152 
     153        public override void Load() 
     154        { 
    147155            //Load all core plugins first 
    148156            foreach (KeyValuePair<string, string> plugin in CorePlugins) 
     
    186194        } 
    187195 
     196        public override IList<PluginInstance> Plugins 
     197        { 
     198            get { return plugins.AsReadOnly(); } 
     199        } 
     200 
     201        /// <summary> 
     202        /// Verifies whether the provided assembly is a plugin. 
     203        /// </summary> 
     204        /// <param name="assembly">The assembly to verify.</param> 
     205        /// <returns>True if the assembly provided is a plugin, false otherwise.</returns> 
     206        private bool IsPlugin(Assembly assembly) 
     207        { 
     208            //Iterate over every exported type, checking if it implements IPlugin 
     209            Type typePlugin = assembly.GetExportedTypes().FirstOrDefault( 
     210                    type => type.GetInterface("Eraser.Plugins.IPlugin", true) != null); 
     211 
     212            //If the typePlugin type is empty the assembly doesn't implement IPlugin it's not 
     213            //a plugin. 
     214            return typePlugin != null; 
     215        } 
     216 
     217        /// <summary> 
     218        /// Loads the assembly at the specified path, and verifying its assembly name, 
     219        /// ensuring that the assembly contains a core plugin. 
     220        /// </summary> 
     221        /// <param name="filePath">The path to the assembly.</param> 
     222        /// <param name="assemblyName">The name of the assembly.</param> 
     223        private void LoadCorePlugin(string filePath, string assemblyName) 
     224        { 
     225            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
     226            if (assembly.GetName().FullName.Substring(0, assemblyName.Length + 1) != 
     227                assemblyName + ",") 
     228            { 
     229                throw new FileLoadException(S._("The Core plugin assembly is not one which" + 
     230                    "Eraser expects.\n\nCheck that the Eraser installation is not corrupt, or " + 
     231                    "reinstall the program.")); 
     232            } 
     233 
     234            //Create the PluginInstance structure 
     235            PluginInstance instance = new PluginInstance(assembly, null); 
     236 
     237            //Ignore non-plugins 
     238            if (!IsPlugin(instance.Assembly)) 
     239                throw new FileLoadException(S._("The provided Core plugin assembly is not a " + 
     240                    "plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall " + 
     241                    "the program.")); 
     242 
     243            //OK this assembly is a plugin 
     244            lock (plugins) 
     245                plugins.Add(instance); 
     246 
     247            //Check for the presence of a valid signature: Core plugins must have the same 
     248            //public key as the current assembly 
     249            if (!assembly.GetName().GetPublicKey().SequenceEqual( 
     250                    Assembly.GetExecutingAssembly().GetName().GetPublicKey())) 
     251            { 
     252                throw new FileLoadException(S._("The provided Core plugin does not have an " + 
     253                    "identical public key as the Eraser assembly.\n\nCheck that the Eraser " + 
     254                    "installation is not corrupt, or reinstall the program.")); 
     255            } 
     256 
     257            //Okay, everything's fine, initialise the plugin 
     258            instance.Assembly = Assembly.Load(instance.Assembly.GetName()); 
     259            instance.LoadingPolicy = LoadingPolicy.Core; 
     260            InitialisePlugin(instance); 
     261        } 
     262 
     263        public override bool LoadPlugin(string filePath) 
     264        { 
     265            //Create the PluginInstance structure 
     266            Assembly reflectAssembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
     267            PluginInstance instance = new PluginInstance(reflectAssembly, null); 
     268 
     269            //Check that the plugin hasn't yet been loaded. 
     270            if (Plugins.Count( 
     271                    plugin => plugin.Assembly.GetName().FullName == 
     272                    reflectAssembly.GetName().FullName) > 0) 
     273            { 
     274                return true; 
     275            } 
     276 
     277            //Ignore non-plugins 
     278            if (!IsPlugin(instance.Assembly)) 
     279                return false; 
     280 
     281            //OK this assembly is a plugin 
     282            lock (plugins) 
     283                plugins.Add(instance); 
     284 
     285            PluginLoadEventArgs e = new PluginLoadEventArgs(instance); 
     286            PluginLoad(this, e); 
     287            if (PluginLoad == null || e.Load) 
     288            { 
     289                InitialisePlugin(instance); 
     290                return true; 
     291            } 
     292 
     293            return false; 
     294        } 
     295 
     296        /// <summary> 
     297        /// Initialises the given plugin from the plugin's description. 
     298        /// </summary> 
     299        /// <param name="instance">The <see cref="PluginInstance"/> structure to fill.</param> 
     300        /// <exception cref="System.IO.FileLoadException" /> 
     301        private void InitialisePlugin(PluginInstance instance) 
     302        { 
     303            try 
     304            { 
     305                //Iterate over every exported type, checking for the IPlugin implementation 
     306                Type typePlugin = instance.Assembly.GetExportedTypes().First( 
     307                    type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 
     308                if (typePlugin == null) 
     309                    return; 
     310 
     311                //Initialize the plugin 
     312                instance.Plugin = (IPlugin)Activator.CreateInstance( 
     313                    instance.Assembly.GetType(typePlugin.ToString())); 
     314                instance.Plugin.Initialize(this); 
     315 
     316                //And broadcast the plugin load event 
     317                OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 
     318            } 
     319            catch (System.Security.SecurityException e) 
     320            { 
     321                throw new FileLoadException(S._("Could not load the plugin."), 
     322                    instance.Assembly.Location, e); 
     323            } 
     324        } 
     325 
     326        private Assembly AssemblyResolve(object sender, ResolveEventArgs args) 
     327        { 
     328            //Check the plugins folder 
     329            foreach (string fileName in Directory.GetFiles(PluginsFolder)) 
     330            { 
     331                FileInfo file = new FileInfo(fileName); 
     332                if (file.Extension.Equals(".dll")) 
     333                    try 
     334                    { 
     335                        Assembly assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 
     336                        if (assembly.GetName().FullName == args.Name) 
     337                            return Assembly.LoadFile(file.FullName); 
     338                    } 
     339                    catch (BadImageFormatException) 
     340                    { 
     341                    } 
     342                    catch (FileLoadException) 
     343                    { 
     344                    } 
     345            } 
     346 
     347            return null; 
     348        } 
     349 
     350        private Assembly ResolveReflectionDependency(object sender, ResolveEventArgs args) 
     351        { 
     352            return Assembly.ReflectionOnlyLoad(args.Name); 
     353        } 
     354 
    188355        /// <summary> 
    189356        /// The path to the folder containing the plugins. 
     
    207374            }; 
    208375 
    209         public override IList<PluginInstance> Plugins 
    210         { 
    211             get { return plugins.AsReadOnly(); } 
    212         } 
    213  
    214         /// <summary> 
    215         /// Verifies whether the provided assembly is a plugin. 
    216         /// </summary> 
    217         /// <param name="assembly">The assembly to verify.</param> 
    218         /// <returns>True if the assembly provided is a plugin, false otherwise.</returns> 
    219         private bool IsPlugin(Assembly assembly) 
    220         { 
    221             //Iterate over every exported type, checking if it implements IPlugin 
    222             Type typePlugin = assembly.GetExportedTypes().FirstOrDefault( 
    223                     type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 
    224  
    225             //If the typePlugin type is empty the assembly doesn't implement IPlugin it's not 
    226             //a plugin. 
    227             return typePlugin != null; 
    228         } 
    229  
    230         /// <summary> 
    231         /// Loads the assembly at the specified path, and verifying its assembly name, 
    232         /// ensuring that the assembly contains a core plugin. 
    233         /// </summary> 
    234         /// <param name="filePath">The path to the assembly.</param> 
    235         /// <param name="assemblyName">The name of the assembly.</param> 
    236         private void LoadCorePlugin(string filePath, string assemblyName) 
    237         { 
    238             Assembly assembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
    239             if (assembly.GetName().FullName.Substring(0, assemblyName.Length + 1) != 
    240                 assemblyName + ",") 
    241             { 
    242                 throw new FileLoadException(S._("The Core plugin assembly is not one which" + 
    243                     "Eraser expects.\n\nCheck that the Eraser installation is not corrupt, or " + 
    244                     "reinstall the program.")); 
    245             } 
    246  
    247             //Create the PluginInstance structure 
    248             PluginInstance instance = new PluginInstance(assembly, null); 
    249  
    250             //Ignore non-plugins 
    251             if (!IsPlugin(instance.Assembly)) 
    252                 throw new FileLoadException(S._("The provided Core plugin assembly is not a " + 
    253                     "plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall " + 
    254                     "the program.")); 
    255  
    256             //OK this assembly is a plugin 
    257             lock (plugins) 
    258                 plugins.Add(instance); 
    259  
    260             //Check for the presence of a valid signature: Core plugins must have the same 
    261             //public key as the current assembly 
    262             if (!assembly.GetName().GetPublicKey().SequenceEqual( 
    263                     Assembly.GetExecutingAssembly().GetName().GetPublicKey())) 
    264             { 
    265                 throw new FileLoadException(S._("The provided Core plugin does not have an " + 
    266                     "identical public key as the Eraser assembly.\n\nCheck that the Eraser " + 
    267                     "installation is not corrupt, or reinstall the program.")); 
    268             } 
    269  
    270             //Okay, everything's fine, initialise the plugin 
    271             instance.Assembly = Assembly.Load(instance.Assembly.GetName()); 
    272             instance.LoadingPolicy = LoadingPolicy.Core; 
    273             InitialisePlugin(instance); 
    274         } 
    275  
    276         public override bool LoadPlugin(string filePath) 
    277         { 
    278             //Create the PluginInstance structure 
    279             Assembly reflectAssembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
    280             PluginInstance instance = new PluginInstance(reflectAssembly, null); 
    281  
    282             //Check that the plugin hasn't yet been loaded. 
    283             if (Plugins.Count( 
    284                     plugin => plugin.Assembly.GetName().FullName == 
    285                     reflectAssembly.GetName().FullName) > 0) 
    286             { 
    287                 return true; 
    288             } 
    289  
    290             //Ignore non-plugins 
    291             if (!IsPlugin(instance.Assembly)) 
    292                 return false; 
    293  
    294             //OK this assembly is a plugin 
    295             lock (plugins) 
    296                 plugins.Add(instance); 
    297  
    298             PluginLoadEventArgs e = new PluginLoadEventArgs(instance); 
    299             PluginLoad(this, e); 
    300             if (PluginLoad == null || e.Load) 
    301             { 
    302                 InitialisePlugin(instance); 
    303                 return true; 
    304             } 
    305  
    306             return false; 
    307         } 
    308  
    309         /// <summary> 
    310         /// Initialises the given plugin from the plugin's description. 
    311         /// </summary> 
    312         /// <param name="instance">The <see cref="PluginInstance"/> structure to fill.</param> 
    313         /// <exception cref="System.IO.FileLoadException" /> 
    314         private void InitialisePlugin(PluginInstance instance) 
    315         { 
    316             try 
    317             { 
    318                 //Iterate over every exported type, checking for the IPlugin implementation 
    319                 Type typePlugin = instance.Assembly.GetExportedTypes().First( 
    320                     type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 
    321                 if (typePlugin == null) 
    322                     return; 
    323  
    324                 //Initialize the plugin 
    325                 instance.Plugin = (IPlugin)Activator.CreateInstance( 
    326                     instance.Assembly.GetType(typePlugin.ToString())); 
    327                 instance.Plugin.Initialize(this); 
    328  
    329                 //And broadcast the plugin load event 
    330                 OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 
    331             } 
    332             catch (System.Security.SecurityException e) 
    333             { 
    334                 throw new FileLoadException(S._("Could not load the plugin."), 
    335                     instance.Assembly.Location, e); 
    336             } 
    337         } 
    338  
    339         private Assembly AssemblyResolve(object sender, ResolveEventArgs args) 
    340         { 
    341             //Check the plugins folder 
    342             foreach (string fileName in Directory.GetFiles(PluginsFolder)) 
    343             { 
    344                 FileInfo file = new FileInfo(fileName); 
    345                 if (file.Extension.Equals(".dll")) 
    346                     try 
    347                     { 
    348                         Assembly assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 
    349                         if (assembly.GetName().FullName == args.Name) 
    350                             return Assembly.LoadFile(file.FullName); 
    351                     } 
    352                     catch (BadImageFormatException) 
    353                     { 
    354                     } 
    355                     catch (FileLoadException) 
    356                     { 
    357                     } 
    358             } 
    359  
    360             return null; 
    361         } 
    362  
    363         private Assembly ResolveReflectionDependency(object sender, ResolveEventArgs args) 
    364         { 
    365             return Assembly.ReflectionOnlyLoad(args.Name); 
    366         } 
    367  
     376        /// <summary> 
     377        /// Stores the list of plugins found within the Plugins folder. 
     378        /// </summary> 
    368379        private List<PluginInstance> plugins = new List<PluginInstance>(); 
    369380    } 
    370  
    371     /// <summary> 
    372     /// Structure holding the instance values of the plugin like handle and path. 
    373     /// </summary> 
    374     public class PluginInstance 
    375     { 
    376         /// <summary> 
    377         /// Constructor 
    378         /// </summary> 
    379         /// <param name="assembly">The assembly representing this plugin.</param> 
    380         /// <param name="path">The path to the ass</param> 
    381         /// <param name="plugin"></param> 
    382         internal PluginInstance(Assembly assembly, IPlugin plugin) 
    383         { 
    384             Assembly = assembly; 
    385             Plugin = plugin; 
    386  
    387             //Verify the certificate in the assembly. 
    388             if (Security.VerifyAuthenticode(assembly.Location)) 
    389             { 
    390                 X509Certificate2 cert = new X509Certificate2( 
    391                     X509Certificate.CreateFromSignedFile(assembly.Location)); 
    392                 AssemblyAuthenticode = cert; 
    393             } 
    394         } 
    395  
    396         /// <summary> 
    397         /// Gets the Assembly this plugin instance came from. 
    398         /// </summary> 
    399         public Assembly Assembly 
    400         { 
    401             get 
    402             { 
    403                 return assembly; 
    404             } 
    405             internal set 
    406             { 
    407                 assembly = value; 
    408  
    409                 AssemblyInfo info = new AssemblyInfo(); 
    410                 info.Version = assembly.GetFileVersion(); 
    411                 IList<CustomAttributeData> attributes = CustomAttributeData.GetCustomAttributes(assembly); 
    412                 foreach (CustomAttributeData attr in attributes) 
    413                     if (attr.Constructor.DeclaringType == typeof(GuidAttribute)) 
    414                         info.Guid = new Guid((string)attr.ConstructorArguments[0].Value); 
    415                     else if (attr.Constructor.DeclaringType == typeof(AssemblyCompanyAttribute)) 
    416                         info.Author = (string)attr.ConstructorArguments[0].Value; 
    417                     else if (attr.Constructor.DeclaringType == typeof(LoadingPolicyAttribute)) 
    418                     { 
    419                         LoadingPolicy = (LoadingPolicy)attr.ConstructorArguments[0].Value; 
    420                         if (LoadingPolicy == LoadingPolicy.Core) 
    421                             LoadingPolicy = LoadingPolicy.None; 
    422                     } 
    423  
    424                 this.AssemblyInfo = info; 
    425             } 
    426         } 
    427  
    428         /// <summary> 
    429         /// Gets the attributes of the assembly, loading from reflection-only sources. 
    430         /// </summary> 
    431         public AssemblyInfo AssemblyInfo { get; private set; } 
    432  
    433         /// <summary> 
    434         /// The Authenticode signature used for signing the assembly. 
    435         /// </summary> 
    436         public X509Certificate2 AssemblyAuthenticode { get; private set; } 
    437  
    438         /// <summary> 
    439         /// Gets whether the plugin is required for the functioning of Eraser (and 
    440         /// therefore cannot be disabled.) 
    441         /// </summary> 
    442         public LoadingPolicy LoadingPolicy { get; internal set; } 
    443  
    444         /// <summary> 
    445         /// Gets the IPlugin interface which the plugin exposed. This may be null 
    446         /// if the plugin was not loaded. 
    447         /// </summary> 
    448         public IPlugin Plugin { get; internal set; } 
    449  
    450         /// <summary> 
    451         /// Gets whether this particular plugin is currently loaded in memory. 
    452         /// </summary> 
    453         public bool Loaded 
    454         { 
    455             get { return Plugin != null; } 
    456         } 
    457  
    458         private Assembly assembly; 
    459     } 
    460  
    461     /// <summary> 
    462     /// Reflection-only information retrieved from the assembly. 
    463     /// </summary> 
    464     public struct AssemblyInfo 
    465     { 
    466         /// <summary> 
    467         /// The GUID of the assembly. 
    468         /// </summary> 
    469         public Guid Guid { get; set; } 
    470  
    471         /// <summary> 
    472         /// The publisher of the assembly. 
    473         /// </summary> 
    474         public string Author { get; set; } 
    475  
    476         /// <summary> 
    477         /// The version of the assembly. 
    478         /// </summary> 
    479         public Version Version { get; set; } 
    480  
    481         public override bool Equals(object obj) 
    482         { 
    483             if (!(obj is AssemblyInfo)) 
    484                 return false; 
    485             return Equals((AssemblyInfo)obj); 
    486         } 
    487  
    488         public bool Equals(AssemblyInfo other) 
    489         { 
    490             return Guid == other.Guid; 
    491         } 
    492  
    493         public static bool operator ==(AssemblyInfo assembly1, AssemblyInfo assembly2) 
    494         { 
    495             return assembly1.Equals(assembly2); 
    496         } 
    497  
    498         public static bool operator !=(AssemblyInfo assembly1, AssemblyInfo assembly2) 
    499         { 
    500             return !assembly1.Equals(assembly2); 
    501         } 
    502  
    503         public override int GetHashCode() 
    504         { 
    505             return Guid.GetHashCode(); 
    506         } 
    507     } 
    508  
    509     /// <summary> 
    510     /// Basic plugin interface which allows for the main program to utilize the 
    511     /// functions in the DLL 
    512     /// </summary> 
    513     public interface IPlugin : IDisposable 
    514     { 
    515         /// <summary> 
    516         /// Initializer. 
    517         /// </summary> 
    518         /// <param name="host">The host object which can be used for two-way 
    519         /// communication with the program.</param> 
    520         void Initialize(Host host); 
    521  
    522         /// <summary> 
    523         /// The name of the plug-in, used for descriptive purposes in the UI 
    524         /// </summary> 
    525         string Name 
    526         { 
    527             get; 
    528         } 
    529  
    530         /// <summary> 
    531         /// The author of the plug-in, used for display in the UI and for users 
    532         /// to contact the author about bugs. Must be in the format: 
    533         ///     (.+) \&lt;([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)\.([a-zA-Z0-9]+)\&gt; 
    534         /// </summary> 
    535         /// <example>Joel Low <joel@joelsplace.sg></example> 
    536         string Author 
    537         { 
    538             get; 
    539         } 
    540  
    541         /// <summary> 
    542         /// Determines whether the plug-in is configurable. 
    543         /// </summary> 
    544         bool Configurable 
    545         { 
    546             get; 
    547         } 
    548  
    549         /// <summary> 
    550         /// Fulfil a request to display the settings for this plug-in. 
    551         /// </summary> 
    552         /// <param name="parent">The parent control which the settings dialog should 
    553         /// be parented with.</param> 
    554         void DisplaySettings(System.Windows.Forms.Control parent); 
    555     } 
    556  
    557     /// <summary> 
    558     /// Loading policies applicable for a given plugin. 
    559     /// </summary> 
    560     public enum LoadingPolicy 
    561     { 
    562         /// <summary> 
    563         /// The host decides the best policy for loading the plugin. 
    564         /// </summary> 
    565         None, 
    566  
    567         /// <summary> 
    568         /// The host will enable the plugin by default. 
    569         /// </summary> 
    570         DefaultOn, 
    571  
    572         /// <summary> 
    573         /// The host will disable the plugin by default 
    574         /// </summary> 
    575         DefaultOff, 
    576  
    577         /// <summary> 
    578         /// The host must always load the plugin. 
    579         /// </summary> 
    580         /// <remarks>This policy does not have an effect when declared in the 
    581         /// <see cref="LoadingPolicyAttribute"/> attribute and will be equivalent 
    582         /// to <see cref="None"/>.</remarks> 
    583         Core 
    584     } 
    585  
    586     /// <summary> 
    587     /// Declares the loading policy for the assembly containing the plugin. Only 
    588     /// plugins signed with an Authenticode signature will be trusted and have 
    589     /// this attribute checked at initialisation. 
    590     /// </summary> 
    591     [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] 
    592     public sealed class LoadingPolicyAttribute : Attribute 
    593     { 
    594         /// <summary> 
    595         /// Constructor. 
    596         /// </summary> 
    597         /// <param name="policy">The policy used for loading the plugin.</param> 
    598         public LoadingPolicyAttribute(LoadingPolicy policy) 
    599         { 
    600             Policy = policy; 
    601         } 
    602  
    603         /// <summary> 
    604         /// The loading policy to be applied to the assembly. 
    605         /// </summary> 
    606         public LoadingPolicy Policy 
    607         { 
    608             get; 
    609             set; 
    610         } 
    611     } 
    612381} 
Note: See TracChangeset for help on using the changeset viewer.