Changeset 2050
- Timestamp:
- 5/4/2010 4:30:46 AM (3 years ago)
- Location:
- trunk/eraser/Eraser.Manager
- Files:
-
- 6 edited
-
Plugins.cs (modified) (7 diffs)
-
Strings.en.resx (modified) (1 diff)
-
Strings.it.resx (modified) (1 diff)
-
Strings.nl.resx (modified) (1 diff)
-
Strings.pl.resx (modified) (1 diff)
-
Strings.resx (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/eraser/Eraser.Manager/Plugins.cs
r2010 r2050 99 99 /// <param name="filePath">The absolute or relative file path to the 100 100 /// DLL.</param> 101 public abstract void LoadPlugin(string filePath); 101 /// <returns>True if the plugin is loaded, false otherwise.</returns> 102 /// <remarks>If a plugin is loaded twice, this function should do nothing 103 /// and return True.</remarks> 104 public abstract bool LoadPlugin(string filePath); 102 105 } 103 106 … … 136 139 public override void Load() 137 140 { 141 //Specify additional places to load assemblies from 138 142 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve; 139 143 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ResolveReflectionDependency; 140 string pluginsFolder = Path.Combine( 141 Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), //Assembly location 142 PLUGINSFOLDER //Plugins folder 143 ); 144 145 foreach (string fileName in Directory.GetFiles(pluginsFolder)) 144 145 try 146 { 147 //Load all core plugins first 148 foreach (KeyValuePair<string, string> plugin in CorePlugins) 149 { 150 LoadCorePlugin(Path.Combine(PluginsFolder, plugin.Key), plugin.Value); 151 } 152 153 //Then load the rest 154 foreach (string fileName in Directory.GetFiles(PluginsFolder)) 155 { 156 FileInfo file = new FileInfo(fileName); 157 if (file.Extension.Equals(".dll")) 158 try 159 { 160 LoadPlugin(file.FullName); 161 } 162 catch (BadImageFormatException) 163 { 164 } 165 catch (FileLoadException) 166 { 167 } 168 } 169 } 170 finally 171 { 172 AppDomain.CurrentDomain.AssemblyResolve -= AssemblyResolve; 173 AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ResolveReflectionDependency; 174 } 175 } 176 177 protected override void Dispose(bool disposing) 178 { 179 if (plugins == null) 180 return; 181 182 if (disposing) 183 { 184 //Unload all the plugins. This will cause all the plugins to execute 185 //the cleanup code. 186 foreach (PluginInstance plugin in plugins) 187 if (plugin.Plugin != null) 188 plugin.Plugin.Dispose(); 189 } 190 191 plugins = null; 192 } 193 194 /// <summary> 195 /// The path to the folder containing the plugins. 196 /// </summary> 197 public readonly string PluginsFolder = Path.Combine( 198 Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), //Assembly location 199 "Plugins" //Plugins folder 200 ); 201 202 /// <summary> 203 /// The list of plugins which are core, the key is the file name, the value 204 /// is the assembly name. 205 /// </summary> 206 private readonly KeyValuePair<string, string>[] CorePlugins = 207 new KeyValuePair<string, string>[] 208 { 209 new KeyValuePair<string, string>( 210 "Eraser.DefaultPlugins.dll", 211 "Eraser.DefaultPlugins" 212 ) 213 }; 214 215 public override IList<PluginInstance> Plugins 216 { 217 get { return plugins.AsReadOnly(); } 218 } 219 220 /// <summary> 221 /// Verifies whether the provided assembly is a plugin. 222 /// </summary> 223 /// <param name="assembly">The assembly to verify.</param> 224 /// <returns>True if the assembly provided is a plugin, false otherwise.</returns> 225 private bool IsPlugin(Assembly assembly) 226 { 227 //Iterate over every exported type, checking if it implements IPlugin 228 Type typePlugin = assembly.GetExportedTypes().FirstOrDefault( 229 type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 230 231 //If the typePlugin type is empty the assembly doesn't implement IPlugin it's not 232 //a plugin. 233 return typePlugin != null; 234 } 235 236 /// <summary> 237 /// Loads the assembly at the specified path, and verifying its assembly name, 238 /// ensuring that the assembly contains a core plugin. 239 /// </summary> 240 /// <param name="filePath">The path to the assembly.</param> 241 /// <param name="assemblyName">The name of the assembly.</param> 242 private void LoadCorePlugin(string filePath, string assemblyName) 243 { 244 Assembly assembly = Assembly.ReflectionOnlyLoadFrom(filePath); 245 if (assembly.GetName().FullName.Substring(0, assemblyName.Length + 1) != 246 assemblyName + ",") 247 { 248 throw new FileLoadException(S._("The Core plugin assembly is not one which" + 249 "Eraser expects.\n\nCheck that the Eraser installation is not corrupt, or " + 250 "reinstall the program.")); 251 } 252 253 //Create the PluginInstance structure 254 PluginInstance instance = new PluginInstance(assembly, null); 255 256 //Ignore non-plugins 257 if (!IsPlugin(instance.Assembly)) 258 throw new FileLoadException(S._("The provided Core plugin assembly is not a " + 259 "plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall " + 260 "the program.")); 261 262 //OK this assembly is a plugin 263 lock (plugins) 264 plugins.Add(instance); 265 266 //Check for the presence of a valid signature: Core plugins must have the same 267 //public key as the current assembly 268 if (!assembly.GetName().GetPublicKey().SequenceEqual( 269 Assembly.GetExecutingAssembly().GetName().GetPublicKey())) 270 { 271 throw new FileLoadException(S._("The provided Core plugin does not have an " + 272 "identical public key as the Eraser assembly.\n\nCheck that the Eraser " + 273 "installation is not corrupt, or reinstall the program.")); 274 } 275 276 //Okay, everything's fine, initialise the plugin 277 instance.Assembly = Assembly.Load(instance.Assembly.GetName()); 278 instance.LoadingPolicy = LoadingPolicy.Core; 279 InitialisePlugin(instance); 280 } 281 282 public override bool LoadPlugin(string filePath) 283 { 284 //Create the PluginInstance structure 285 Assembly reflectAssembly = Assembly.ReflectionOnlyLoadFrom(filePath); 286 PluginInstance instance = new PluginInstance(reflectAssembly, null); 287 288 //Check that the plugin hasn't yet been loaded. 289 if (Plugins.Count( 290 plugin => plugin.Assembly.GetName().FullName == 291 reflectAssembly.GetName().FullName) > 0) 292 { 293 return true; 294 } 295 296 //Ignore non-plugins 297 if (!IsPlugin(instance.Assembly)) 298 return false; 299 300 //OK this assembly is a plugin 301 lock (plugins) 302 plugins.Add(instance); 303 304 //If the plugin does not have an approval or denial, check for the presence of 305 //a valid signature. 306 IDictionary<Guid, bool> approvals = ManagerLibrary.Settings.PluginApprovals; 307 if (!approvals.ContainsKey(instance.AssemblyInfo.Guid) && 308 (reflectAssembly.GetName().GetPublicKey().Length == 0 || 309 !Security.VerifyStrongName(filePath) || 310 instance.AssemblyAuthenticode == null)) 311 { 312 return false; 313 } 314 315 //Preliminary checks to verify whether the plugin can be loaded (safely) passes, 316 //Load the assembly fully, and then initialise it. 317 instance.Assembly = Assembly.Load(reflectAssembly.GetName()); 318 319 //The plugin either is explicitly allowed or disallowed to load, or 320 //it has an Authenticode Signature as well as a Strong Name. Get the 321 //loading policy of the plugin. 322 { 323 324 } 325 326 bool initialisePlugin = false; 327 328 //Is there an approval or denial? 329 if (approvals.ContainsKey(instance.AssemblyInfo.Guid)) 330 initialisePlugin = approvals[instance.AssemblyInfo.Guid]; 331 332 //There's no approval or denial, what is the specified loading policy? 333 else 334 initialisePlugin = instance.LoadingPolicy != LoadingPolicy.DefaultOff; 335 336 if (initialisePlugin) 337 { 338 InitialisePlugin(instance); 339 return true; 340 } 341 342 return false; 343 } 344 345 /// <summary> 346 /// Initialises the given plugin from the plugin's description. 347 /// </summary> 348 /// <param name="instance">The <see cref="PluginInstance"/> structure to fill.</param> 349 private void InitialisePlugin(PluginInstance instance) 350 { 351 try 352 { 353 //Iterate over every exported type, checking for the IPlugin implementation 354 Type typePlugin = instance.Assembly.GetExportedTypes().First( 355 type => type.GetInterface("Eraser.Manager.Plugin.IPlugin", true) != null); 356 if (typePlugin == null) 357 return; 358 359 //Initialize the plugin 360 instance.Plugin = (IPlugin)Activator.CreateInstance( 361 instance.Assembly.GetType(typePlugin.ToString())); 362 instance.Plugin.Initialize(this); 363 364 //And broadcast the plugin load event 365 OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 366 } 367 catch (System.Security.SecurityException e) 368 { 369 MessageBox.Show(S._("Could not load the plugin {0}.\n\nThe error returned was: {1}", 370 instance.Assembly.Location, e.Message), S._("Eraser"), MessageBoxButtons.OK, 371 MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 372 Localisation.IsRightToLeft(null) ? 373 MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0); 374 } 375 } 376 377 private Assembly AssemblyResolve(object sender, ResolveEventArgs args) 378 { 379 //Check the plugins folder 380 foreach (string fileName in Directory.GetFiles(PluginsFolder)) 146 381 { 147 382 FileInfo file = new FileInfo(fileName); … … 149 384 try 150 385 { 151 LoadPlugin(file.FullName); 386 Assembly assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 387 if (assembly.GetName().FullName == args.Name) 388 return Assembly.LoadFile(file.FullName); 152 389 } 153 390 catch (BadImageFormatException) … … 158 395 } 159 396 } 160 } 161 162 protected override void Dispose(bool disposing) 163 { 164 if (plugins == null) 165 return; 166 167 if (disposing) 168 { 169 //Unload all the plugins. This will cause all the plugins to execute 170 //the cleanup code. 171 foreach (PluginInstance plugin in plugins) 172 if (plugin.Plugin != null) 173 plugin.Plugin.Dispose(); 174 } 175 176 plugins = null; 177 } 178 179 /// <summary> 180 /// The path to the folder containing the plugins. 181 /// </summary> 182 public const string PLUGINSFOLDER = "Plugins"; 183 184 public override IList<PluginInstance> Plugins 185 { 186 get { return plugins.AsReadOnly(); } 187 } 188 189 public override void LoadPlugin(string filePath) 190 { 191 //Create the PluginInstance structure 192 Assembly reflectAssembly = Assembly.ReflectionOnlyLoadFrom(filePath); 193 PluginInstance instance = new PluginInstance(reflectAssembly, null); 194 Type typePlugin = null; 195 196 //Iterate over every exported type, checking if it implements IPlugin 197 foreach (Type type in instance.Assembly.GetExportedTypes()) 198 { 199 //Check for an implementation of IPlugin 200 Type typeInterface = type.GetInterface("Eraser.Manager.Plugin.IPlugin", true); 201 if (typeInterface != null) 202 { 203 typePlugin = type; 204 break; 205 } 206 } 207 208 //If the typePlugin type is empty the assembly doesn't implement IPlugin; we 209 //aren't interested. 210 if (typePlugin == null) 211 return; 212 213 //OK this assembly is a plugin 214 lock (plugins) 215 plugins.Add(instance); 216 217 //If the plugin does not have an approval or denial, check for the presence of 218 //a valid signature. 219 IDictionary<Guid, bool> approvals = ManagerLibrary.Settings.PluginApprovals; 220 if (!approvals.ContainsKey(instance.AssemblyInfo.Guid) && 221 (reflectAssembly.GetName().GetPublicKey().Length == 0 || 222 !Security.VerifyStrongName(filePath) || 223 instance.AssemblyAuthenticode == null)) 224 { 225 return; 226 } 227 228 //The plugin either is explicitly allowed or disallowed to load, or 229 //it has an Authenticode Signature as well as a Strong Name. Get the 230 //loading policy of the plugin. 231 instance.Assembly = Assembly.LoadFrom(filePath); 232 { 233 object[] attr = instance.Assembly.GetCustomAttributes(typeof(LoadingPolicyAttribute), true); 234 if (attr.Length != 0) 235 { 236 instance.LoadingPolicy = ((LoadingPolicyAttribute)attr[0]).Policy; 237 238 //If the loading policy is that the plugin is Core, we need to verify 239 //the public key of the assembly. 240 if (instance.LoadingPolicy == LoadingPolicy.Core && 241 !reflectAssembly.GetName().GetPublicKey().SequenceEqual( 242 Assembly.GetExecutingAssembly().GetName().GetPublicKey())) 243 { 244 instance.LoadingPolicy = LoadingPolicy.None; 245 } 246 } 247 } 248 249 bool loadPlugin = false; 250 251 //If the loading policy is such that the plugin is a core plugin, ALWAYS load it. 252 if (instance.LoadingPolicy == LoadingPolicy.Core) 253 loadPlugin = true; 254 255 //The plugin is not a core plugin, is there an approval or denial? 256 else if (approvals.ContainsKey(instance.AssemblyInfo.Guid)) 257 loadPlugin = approvals[instance.AssemblyInfo.Guid]; 258 259 //There's no approval or denial, what is the specified loading policy? 260 else 261 loadPlugin = instance.LoadingPolicy != LoadingPolicy.DefaultOff; 262 263 264 if (loadPlugin) 265 { 266 try 267 { 268 //Initialize the plugin 269 IPlugin pluginInterface = (IPlugin)Activator.CreateInstance( 270 instance.Assembly.GetType(typePlugin.ToString())); 271 pluginInterface.Initialize(this); 272 instance.Plugin = pluginInterface; 273 274 //And broadcast the plugin load event 275 OnPluginLoaded(this, new PluginLoadedEventArgs(instance)); 276 } 277 catch (System.Security.SecurityException e) 278 { 279 MessageBox.Show(S._("Could not load the plugin {0}.\n\nThe error returned was: {1}", 280 filePath, e.Message), S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Error, 281 MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(null) ? 282 MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0); 283 } 284 } 285 } 286 287 private Assembly AssemblyResolve(object sender, ResolveEventArgs args) 288 { 289 lock (plugins) 290 foreach (PluginInstance instance in plugins) 291 if (instance.Assembly.FullName == args.Name) 292 return instance.Assembly; 397 293 398 return null; 294 399 } … … 348 453 else if (attr.Constructor.DeclaringType == typeof(AssemblyCompanyAttribute)) 349 454 info.Author = (string)attr.ConstructorArguments[0].Value; 455 else if (attr.Constructor.DeclaringType == typeof(LoadingPolicyAttribute)) 456 { 457 LoadingPolicy = (LoadingPolicy)attr.ConstructorArguments[0].Value; 458 if (LoadingPolicy == LoadingPolicy.Core) 459 LoadingPolicy = LoadingPolicy.None; 460 } 350 461 351 462 this.AssemblyInfo = info; … … 458 569 /// The author of the plug-in, used for display in the UI and for users 459 570 /// to contact the author about bugs. Must be in the format: 460 /// (.+) \ <([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)\.([a-zA-Z0-9]+)\>571 /// (.+) \<([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)\.([a-zA-Z0-9]+)\> 461 572 /// </summary> 462 573 /// <example>Joel Low <joel@joelsplace.sg></example> … … 505 616 /// The host must always load the plugin. 506 617 /// </summary> 507 /// <remarks> For this policy to have an effect, the plugin assembly must508 /// have the same Strong Name as the loading assembly, otherwise it defaults509 /// to None.</remarks>618 /// <remarks>This policy does not have an effect when declared in the 619 /// <see cref="LoadingPolicyAttribute"/> attribute and will be equivalent 620 /// to <see cref="None"/>.</remarks> 510 621 Core 511 622 } -
trunk/eraser/Eraser.Manager/Strings.en.resx
r2036 r2050 148 148 <value>The file system on the drive {0} is not supported.</value> 149 149 </data> 150 <data name="The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 151 <value>The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 152 </data> 153 <data name="The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 154 <value>The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 155 </data> 156 <data name="The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 157 <value>The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 158 </data> 150 159 <data name="Could not load the plugin {0}.\n\nThe error returned was: {1}" xml:space="preserve"> 151 160 <value>Could not load the plugin {0}.\n\nThe error returned was: {1}</value> -
trunk/eraser/Eraser.Manager/Strings.it.resx
r2036 r2050 148 148 <value>Il tipo di file system sul disco {0} non è supportato.</value> 149 149 </data> 150 <data name="The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 151 <value>(Untranslated)</value> 152 </data> 153 <data name="The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 154 <value>(Untranslated)</value> 155 </data> 156 <data name="The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 157 <value>(Untranslated)</value> 158 </data> 150 159 <data name="Could not load the plugin {0}.\n\nThe error returned was: {1}" xml:space="preserve"> 151 160 <value>Impossibile caricare il plugin {0}.\n\nL'errore ricevuto è stato: {1}</value> -
trunk/eraser/Eraser.Manager/Strings.nl.resx
r2036 r2050 148 148 <value>(Untranslated)</value> 149 149 </data> 150 <data name="The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 151 <value>(Untranslated)</value> 152 </data> 153 <data name="The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 154 <value>(Untranslated)</value> 155 </data> 156 <data name="The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 157 <value>(Untranslated)</value> 158 </data> 150 159 <data name="Could not load the plugin {0}.\n\nThe error returned was: {1}" xml:space="preserve"> 151 160 <value>(Untranslated)</value> -
trunk/eraser/Eraser.Manager/Strings.pl.resx
r2036 r2050 148 148 <value>System plików napędu {0} nie jest wspierany.</value> 149 149 </data> 150 <data name="The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 151 <value>(Untranslated)</value> 152 </data> 153 <data name="The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 154 <value>(Untranslated)</value> 155 </data> 156 <data name="The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 157 <value>(Untranslated)</value> 158 </data> 150 159 <data name="Could not load the plugin {0}.\n\nThe error returned was: {1}" xml:space="preserve"> 151 160 <value>Nie można było załadować wtyczki {0}.\n\nWystąpił błąd: {1}</value> -
trunk/eraser/Eraser.Manager/Strings.resx
r2036 r2050 148 148 <value>The file system on the drive {0} is not supported.</value> 149 149 </data> 150 <data name="The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 151 <value>The Core plugin assembly is not one whichEraser expects.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 152 </data> 153 <data name="The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 154 <value>The provided Core plugin assembly is not a plugin.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 155 </data> 156 <data name="The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program." xml:space="preserve"> 157 <value>The provided Core plugin does not have an identical public key as the Eraser assembly.\n\nCheck that the Eraser installation is not corrupt, or reinstall the program.</value> 158 </data> 150 159 <data name="Could not load the plugin {0}.\n\nThe error returned was: {1}" xml:space="preserve"> 151 160 <value>Could not load the plugin {0}.\n\nThe error returned was: {1}</value>
Note: See TracChangeset
for help on using the changeset viewer.
