Ignore:
Timestamp:
1/4/2011 7:05:39 AM (4 years ago)
Author:
lowjoel
Message:
  • Fixed the PluginInfo?.Load function to not load the assembly twice if it was loaded normally and not using the reflection-only context (this rule was relaxed in the newest Host implementation)
  • Core plugins will still be loaded before the rest of the plugins, but they will be loaded only by assembly name and not by path
  • Plugin initialisation is handled by the PluginInfo? class as initialisation modifies properties in the PluginInfo? class
  • Non-core plugins call a callback function provided by the host owner to determine if they will be loaded by the host
  • AssemblyResolve? will now support matching of one or more components found in the full name of an assembly name
Location:
branches/eraser6/pluginsRewrite/Eraser.Plugins
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/eraser6/pluginsRewrite/Eraser.Plugins/Host.cs

    r2292 r2295  
    9090        /// </summary> 
    9191        /// <remarks>The returned list is read-only</remarks> 
    92         public abstract IList<PluginInstance> Plugins 
     92        public abstract IList<PluginInfo> Plugins 
    9393        { 
    9494            get; 
     
    149149        { 
    150150            //Load all core plugins first 
    151             foreach (KeyValuePair<string, string> plugin in CorePlugins) 
    152             { 
    153                 LoadCorePlugin(Path.Combine(PluginsFolder, plugin.Key), plugin.Value); 
     151            foreach (string name in CorePlugins) 
     152            { 
     153                if (!LoadPlugin(new AssemblyName(name))) 
     154                    throw new FileLoadException(S._("The required Core plugin {0} could not be " + 
     155                        "loaded. Repair the Eraser installation and try again.")); 
    154156            } 
    155157 
     
    181183                //Unload all the plugins. This will cause all the plugins to execute 
    182184                //the cleanup code. 
    183                 foreach (PluginInstance plugin in plugins) 
     185                foreach (PluginInfo plugin in plugins) 
    184186                    if (plugin.Plugin != null) 
    185187                        plugin.Plugin.Dispose(); 
     
    189191        } 
    190192 
    191         public override IList<PluginInstance> Plugins 
     193        public override IList<PluginInfo> Plugins 
    192194        { 
    193195            get { return plugins.AsReadOnly(); } 
     
    205207                    type => type.GetInterface("Eraser.Plugins.IPlugin", true) != null); 
    206208 
    207             //If the typePlugin type is empty the assembly doesn't implement IPlugin it's not 
    208             //a plugin. 
     209            //If the typePlugin type is empty, the assembly doesn't implement IPlugin and thus 
     210            //it is not a plugin. 
    209211            return typePlugin != null; 
    210212        } 
    211213 
    212         /// <summary> 
    213         /// Loads the assembly at the specified path, and verifying its assembly name, 
    214         /// ensuring that the assembly contains a core plugin. 
    215         /// </summary> 
    216         /// <param name="filePath">The path to the assembly.</param> 
    217         /// <param name="assemblyName">The name of the assembly.</param> 
    218         private void LoadCorePlugin(string filePath, string assemblyName) 
    219         { 
    220             Assembly assembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
    221             if (assembly.GetName().FullName.Substring(0, assemblyName.Length + 1) != 
    222                 assemblyName + ",") 
    223             { 
    224                 throw new FileLoadException(S._("The Core plugin assembly is not one which" + 
    225                     "Eraser expects.\n\nCheck that the Eraser installation is not corrupt, or " + 
    226                     "reinstall the program.")); 
    227             } 
    228  
    229             //Create the PluginInstance structure 
    230             PluginInstance instance = new PluginInstance(assembly, null); 
     214        public bool LoadPlugin(AssemblyName name) 
     215        { 
     216            //Check the plugins folder 
     217            foreach (string fileName in Directory.GetFiles(PluginsFolder)) 
     218            { 
     219                FileInfo file = new FileInfo(fileName); 
     220                if (file.Extension.Equals(".dll")) 
     221                    try 
     222                    { 
     223                        Assembly assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 
     224                        if (AssemblyMatchesName(assembly, name)) 
     225                        { 
     226                            return LoadPlugin(assembly); 
     227                        } 
     228                    } 
     229                    catch (BadImageFormatException) 
     230                    { 
     231                    } 
     232                    catch (FileLoadException) 
     233                    { 
     234                    } 
     235            } 
     236 
     237            return false; 
     238        } 
     239 
     240        public override bool LoadPlugin(string filePath) 
     241        { 
     242            return LoadPlugin(Assembly.ReflectionOnlyLoadFrom(filePath)); 
     243        } 
     244 
     245        /// <summary> 
     246        /// Checks the provided assembly for its name and attempts to load it using the 
     247        /// plugin loading rules. 
     248        /// </summary> 
     249        /// <param name="assembly">The plugin to load. This assembly can be loaded 
     250        /// in the reflection-only context for security.</param> 
     251        /// <returns>True if the assembly was a plugin and loaded without error.</returns> 
     252        private bool LoadPlugin(Assembly assembly) 
     253        { 
     254            PluginInfo instance = new PluginInfo(assembly, null); 
     255 
     256            //Check that the plugin hasn't yet been loaded. 
     257            if (Plugins.Count( 
     258                    plugin => plugin.Assembly.GetName().FullName == 
     259                    assembly.GetName().FullName) > 0) 
     260            { 
     261                return true; 
     262            } 
    231263 
    232264            //Ignore non-plugins 
    233265            if (!IsPlugin(instance.Assembly)) 
    234                 throw new FileLoadException(S._("The provided Core plugin assembly is not a " + 
    235                     "plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall " + 
    236                     "the program.")); 
     266                return false; 
    237267 
    238268            //OK this assembly is a plugin 
     
    240270                plugins.Add(instance); 
    241271 
     272            //Load the plugin, depending on type 
     273            bool result = instance.LoadingPolicy == LoadingPolicy.Core ? 
     274                LoadCorePlugin(instance) : LoadNonCorePlugin(instance); 
     275            if (result) 
     276            { 
     277                //And broadcast the plugin load event 
     278                OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 
     279            } 
     280 
     281            return result; 
     282        } 
     283 
     284        /// <summary> 
     285        /// Verifies the assembly name and strong name of a plugin, ensuring that the assembly 
     286        /// contains a core plugin before loading and initialising it. 
     287        /// </summary> 
     288        /// <param name="info">The plugin to load.</param> 
     289        /// <returns>True if the plugin was loaded.</returns> 
     290        private bool LoadCorePlugin(PluginInfo info) 
     291        { 
     292            //Check that this plugin's name appears in our list of core plugins, otherwise this 
     293            //is a phony 
     294            if (CorePlugins.Count(x => x == info.Assembly.GetName().Name) == 0) 
     295                return LoadNonCorePlugin(info); 
     296 
    242297            //Check for the presence of a valid signature: Core plugins must have the same 
    243298            //public key as the current assembly 
    244             if (!assembly.GetName().GetPublicKey().SequenceEqual( 
     299            if (!info.Assembly.GetName().GetPublicKey().SequenceEqual( 
    245300                    Assembly.GetExecutingAssembly().GetName().GetPublicKey())) 
    246301            { 
     
    250305            } 
    251306 
    252             //Okay, everything's fine, initialise the plugin 
    253             instance.Assembly = Assembly.Load(instance.Assembly.GetName()); 
    254             instance.LoadingPolicy = LoadingPolicy.Core; 
    255             InitialisePlugin(instance); 
    256         } 
    257  
    258         public override bool LoadPlugin(string filePath) 
    259         { 
    260             //Create the PluginInstance structure 
    261             Assembly reflectAssembly = Assembly.ReflectionOnlyLoadFrom(filePath); 
    262             PluginInstance instance = new PluginInstance(reflectAssembly, null); 
    263  
    264             //Check that the plugin hasn't yet been loaded. 
    265             if (Plugins.Count( 
    266                     plugin => plugin.Assembly.GetName().FullName == 
    267                     reflectAssembly.GetName().FullName) > 0) 
    268             { 
    269                 return true; 
    270             } 
    271  
    272             //Ignore non-plugins 
    273             if (!IsPlugin(instance.Assembly)) 
    274                 return false; 
    275  
    276             //OK this assembly is a plugin 
    277             lock (plugins) 
    278                 plugins.Add(instance); 
    279  
    280             PluginLoadEventArgs e = new PluginLoadEventArgs(instance); 
    281             PluginLoad(this, e); 
    282             if (PluginLoad == null || e.Load) 
    283             { 
    284                 InitialisePlugin(instance); 
    285                 return true; 
    286             } 
    287  
    288             return false; 
    289         } 
    290  
    291         /// <summary> 
    292         /// Initialises the given plugin from the plugin's description. 
    293         /// </summary> 
    294         /// <param name="instance">The <see cref="PluginInstance"/> structure to fill.</param> 
    295         /// <exception cref="System.IO.FileLoadException" /> 
    296         private void InitialisePlugin(PluginInstance instance) 
    297         { 
    298             try 
    299             { 
    300                 //Iterate over every exported type, checking for the IPlugin implementation 
    301                 Type typePlugin = instance.Assembly.GetExportedTypes().First( 
    302                     type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 
    303                 if (typePlugin == null) 
    304                     return; 
    305  
    306                 //Initialize the plugin 
    307                 instance.Plugin = (IPlugin)Activator.CreateInstance( 
    308                     instance.Assembly.GetType(typePlugin.ToString())); 
    309                 instance.Plugin.Initialize(this); 
    310  
    311                 //And broadcast the plugin load event 
    312                 OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 
    313             } 
    314             catch (System.Security.SecurityException e) 
    315             { 
    316                 throw new FileLoadException(S._("Could not load the plugin."), 
    317                     instance.Assembly.Location, e); 
    318             } 
     307            //Load the plugin. 
     308            info.Load(this); 
     309            return true; 
     310        } 
     311 
     312        /// <summary> 
     313        /// Queries the Plugin Host's owner on whether to load the current plugin. 
     314        /// </summary> 
     315        /// <param name="info">The plugin to load.</param> 
     316        /// <returns>True if the plugin was loaded.</returns> 
     317        private bool LoadNonCorePlugin(PluginInfo info) 
     318        { 
     319            PluginLoadEventArgs e = new PluginLoadEventArgs(info); 
     320            if (PluginLoad != null) 
     321                PluginLoad(this, e); 
     322             
     323            if (e.Load) 
     324                info.Load(this); 
     325 
     326            return e.Load; 
     327        } 
     328 
     329        private static bool AssemblyMatchesName(Assembly assembly, AssemblyName name) 
     330        { 
     331            AssemblyName assemblyName = assembly.GetName(); 
     332            return (name.Name == assemblyName.Name && 
     333                (name.Version == null || name.Version == assemblyName.Version) && 
     334                (name.ProcessorArchitecture == ProcessorArchitecture.None || name.ProcessorArchitecture == assemblyName.ProcessorArchitecture) && 
     335                (name.GetPublicKey() == null || name.GetPublicKey().SequenceEqual(assemblyName.GetPublicKey())) 
     336            ); 
    319337        } 
    320338 
    321339        private Assembly AssemblyResolve(object sender, ResolveEventArgs args) 
    322340        { 
     341            //Parse the assembly name 
     342            AssemblyName name = new AssemblyName(args.Name); 
     343 
    323344            //Check the plugins folder 
    324345            foreach (string fileName in Directory.GetFiles(PluginsFolder)) 
     
    329350                    { 
    330351                        Assembly assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 
    331                         if (assembly.GetName().FullName == args.Name) 
     352                        if (AssemblyMatchesName(assembly, name)) 
     353                        { 
    332354                            return Assembly.LoadFile(file.FullName); 
     355                        } 
    333356                    } 
    334357                    catch (BadImageFormatException) 
     
    357380 
    358381        /// <summary> 
    359         /// The list of plugins which are core, the key is the file name, the value 
    360         /// is the assembly name. 
    361         /// </summary> 
    362         private readonly KeyValuePair<string, string>[] CorePlugins = 
    363             new KeyValuePair<string, string>[] 
    364             { 
    365                 new KeyValuePair<string, string>( 
    366                     "Eraser.DefaultPlugins.dll", 
    367                     "Eraser.DefaultPlugins" 
    368                 ) 
    369             }; 
     382        /// The list of plugins which are core. This list contains the names of every 
     383        /// assembly which are expected to be core plugins. 
     384        /// </summary> 
     385        private readonly string[] CorePlugins = new string[] { 
     386            "Eraser.DefaultPlugins" 
     387        }; 
    370388 
    371389        /// <summary> 
    372390        /// Stores the list of plugins found within the Plugins folder. 
    373391        /// </summary> 
    374         private List<PluginInstance> plugins = new List<PluginInstance>(); 
     392        private List<PluginInfo> plugins = new List<PluginInfo>(); 
    375393    } 
    376394} 
  • branches/eraser6/pluginsRewrite/Eraser.Plugins/PluginInfo.cs

    r2293 r2295  
    6767        internal void Load(Host host) 
    6868        { 
    69             Assembly = Assembly.Load(Assembly.GetName()); 
     69            if (Assembly.ReflectionOnly) 
     70                Assembly = Assembly.Load(Assembly.GetName()); 
    7071 
    7172            try 
     
    7374                //Iterate over every exported type, checking for the IPlugin implementation 
    7475                Type typePlugin = Assembly.GetExportedTypes().First( 
    75                     type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 
    76                 if (typePlugin == null) 
    77                     throw new FileLoadException(S._("Could not load the plugin."), 
    78                         Assembly.Location); 
     76                    type => type.GetInterface("Eraser.Plugins.IPlugin", true) != null); 
    7977 
    8078                //Initialize the plugin 
    81                 Plugin = (IPlugin)Activator.CreateInstance(Assembly.GetType(typePlugin.ToString())); 
     79                Plugin = (IPlugin)Activator.CreateInstance(typePlugin); 
    8280                Plugin.Initialize(host); 
     81            } 
     82            catch (InvalidOperationException e) 
     83            { 
     84                throw new FileLoadException(S._("Could not load the plugin."), 
     85                    Assembly.Location, e); 
    8386            } 
    8487            catch (System.Security.SecurityException e) 
Note: See TracChangeset for help on using the changeset viewer.