Changeset 2291
- Timestamp:
- 1/4/2011 5:38:18 AM (2 years ago)
- Location:
- branches/eraser6/pluginsRewrite
- Files:
-
- 4 added
- 1 edited
- 1 moved
-
Eraser.Manager/ManagerLibrary.cs (modified) (1 diff)
-
Eraser.Plugins/AssemblyInfo.cs (added)
-
Eraser.Plugins/Host.cs (moved) (moved from branches/eraser6/pluginsRewrite/Eraser.Plugins/Plugins.cs) (4 diffs)
-
Eraser.Plugins/IPlugin.cs (added)
-
Eraser.Plugins/LoadingPolicy.cs (added)
-
Eraser.Plugins/PluginInstance.cs (added)
Legend:
- Unmodified
- Added
- Removed
-
branches/eraser6/pluginsRewrite/Eraser.Manager/ManagerLibrary.cs
r2288 r2291 50 50 ErasureTargetRegistrar = new ErasureTargetRegistrar(); 51 51 FileSystemRegistrar = new FileSystemRegistrar(); 52 Host.Initialise(); 53 Host.Instance.Load(); 52 54 } 53 55 -
branches/eraser6/pluginsRewrite/Eraser.Plugins/Host.cs
r2288 r2291 61 61 62 62 /// <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> 63 73 /// Constructor. Sets the global Plugin Host instance. 64 74 /// </summary> … … 132 142 { 133 143 /// <summary> 134 /// Constructor. Loads all plugins in the Plugins folder.144 /// Constructor. 135 145 /// </summary> 136 146 public DefaultHost() : base() 137 {138 Load();139 }140 141 public override void Load()142 147 { 143 148 //Specify additional places to load assemblies from 144 149 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; 145 150 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ResolveReflectionDependency; 146 151 } 152 153 public override void Load() 154 { 147 155 //Load all core plugins first 148 156 foreach (KeyValuePair<string, string> plugin in CorePlugins) … … 186 194 } 187 195 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 188 355 /// <summary> 189 356 /// The path to the folder containing the plugins. … … 207 374 }; 208 375 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> 368 379 private List<PluginInstance> plugins = new List<PluginInstance>(); 369 380 } 370 371 /// <summary>372 /// Structure holding the instance values of the plugin like handle and path.373 /// </summary>374 public class PluginInstance375 {376 /// <summary>377 /// Constructor378 /// </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 Assembly400 {401 get402 {403 return assembly;404 }405 internal set406 {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 (and440 /// 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 null446 /// 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 Loaded454 {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 AssemblyInfo465 {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 the511 /// functions in the DLL512 /// </summary>513 public interface IPlugin : IDisposable514 {515 /// <summary>516 /// Initializer.517 /// </summary>518 /// <param name="host">The host object which can be used for two-way519 /// 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 UI524 /// </summary>525 string Name526 {527 get;528 }529 530 /// <summary>531 /// The author of the plug-in, used for display in the UI and for users532 /// to contact the author about bugs. Must be in the format:533 /// (.+) \<([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)\.([a-zA-Z0-9]+)\>534 /// </summary>535 /// <example>Joel Low <joel@joelsplace.sg></example>536 string Author537 {538 get;539 }540 541 /// <summary>542 /// Determines whether the plug-in is configurable.543 /// </summary>544 bool Configurable545 {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 should553 /// 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 LoadingPolicy561 {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 default574 /// </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 the581 /// <see cref="LoadingPolicyAttribute"/> attribute and will be equivalent582 /// to <see cref="None"/>.</remarks>583 Core584 }585 586 /// <summary>587 /// Declares the loading policy for the assembly containing the plugin. Only588 /// plugins signed with an Authenticode signature will be trusted and have589 /// this attribute checked at initialisation.590 /// </summary>591 [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]592 public sealed class LoadingPolicyAttribute : Attribute593 {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 Policy607 {608 get;609 set;610 }611 }612 381 }
Note: See TracChangeset
for help on using the changeset viewer.
