PageRenderTime 1469ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Effects/EffectsCollection.cs

https://bitbucket.org/tuldok89/openpdn
C# | 331 lines | 234 code | 57 blank | 40 comment | 22 complexity | c852e027c6d348822b3980c53eb0b7c9 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Reflection;
  13. using System.Runtime.InteropServices;
  14. using PaintDotNet.Base;
  15. namespace PaintDotNet.Effects
  16. {
  17. public sealed class EffectsCollection
  18. {
  19. private readonly Assembly[] _assemblies;
  20. private List<Type> _effects;
  21. // Assembly, TypeName, Exception[]
  22. private readonly List<Triple<Assembly, Type, Exception>> _loaderExceptions =
  23. new List<Triple<Assembly, Type, Exception>>();
  24. private void AddLoaderException(Triple<Assembly, Type, Exception> loaderException)
  25. {
  26. lock (this)
  27. {
  28. _loaderExceptions.Add(loaderException);
  29. }
  30. }
  31. public Triple<Assembly, Type, Exception>[] GetLoaderExceptions()
  32. {
  33. lock (this)
  34. {
  35. return _loaderExceptions.ToArray();
  36. }
  37. }
  38. public EffectsCollection(List<Assembly> assemblies)
  39. {
  40. _assemblies = assemblies.ToArray();
  41. _effects = null;
  42. }
  43. public EffectsCollection(IEnumerable<Type> effects)
  44. {
  45. _assemblies = null;
  46. _effects = new List<Type>(effects);
  47. }
  48. public Type[] Effects
  49. {
  50. get
  51. {
  52. lock (this)
  53. {
  54. if (_effects == null)
  55. {
  56. var errors = new List<Triple<Assembly, Type, Exception>>();
  57. _effects = GetEffectsFromAssemblies(_assemblies, errors);
  58. foreach (Triple<Assembly, Type, Exception> t in errors)
  59. {
  60. AddLoaderException(t);
  61. }
  62. }
  63. }
  64. return _effects.ToArray();
  65. }
  66. }
  67. private static Version GetAssemblyVersionFromType(Type type)
  68. {
  69. try
  70. {
  71. Assembly assembly = type.Assembly;
  72. var assemblyName = new AssemblyName(assembly.FullName);
  73. return assemblyName.Version;
  74. }
  75. catch (Exception)
  76. {
  77. return new Version(0, 0, 0, 0);
  78. }
  79. }
  80. private static bool CheckForGuidOnType(Type type, Guid guid)
  81. {
  82. try
  83. {
  84. object[] attributes = type.GetCustomAttributes(typeof(GuidAttribute), true);
  85. if (attributes.Cast<GuidAttribute>().Any(guidAttr => new Guid(guidAttr.Value) == guid))
  86. {
  87. return true;
  88. }
  89. }
  90. catch (Exception)
  91. {
  92. }
  93. return false;
  94. }
  95. private static bool CheckForAnyGuidOnType(Type type, IEnumerable<Guid> guids)
  96. {
  97. return guids.Any(guid => CheckForGuidOnType(type, guid));
  98. }
  99. private static readonly Guid[] DeprecatedEffectGuids =
  100. new[]
  101. {
  102. new Guid("9A1EB3D9-0A36-4d32-9BB2-707D6E5A9D2C"), // TwistEffect from old DistortionEffects.dll
  103. new Guid("3154E367-6B4D-4960-B4D8-F6D06E1C9C24"), // TileEffect from old DistortionEffects.dll
  104. new Guid("1445F876-356D-4a7c-B726-50457F6E7AEF"), // PolarInversionEffect or BulgeEffect from old DistortionEffects.dll (accidentally put same guid on both)
  105. new Guid("270DCBF1-CE42-411e-9885-162E2BFA8265"), // GlowEffect from old GlowEffect.dll
  106. };
  107. private static string UnstableMessage
  108. {
  109. get
  110. {
  111. return PdnResources.GetString("BlockedPluginException.UnstablePlugin");
  112. }
  113. }
  114. private static string BuiltInMessage
  115. {
  116. get
  117. {
  118. return PdnResources.GetString("BlockedPluginException.PluginIsNowBuiltIn");
  119. }
  120. }
  121. // effectNamespace, effectName, maxVersion, reason
  122. private static readonly Quadruple<string, string, Version, string>[] BlockedEffects =
  123. new[]
  124. {
  125. // pyrochild's Film effect, v1.0.0.0, does some seriously weird stuff that hard crashes us no matter what
  126. Quadruple.Create("FilmEffect", "FilmEffect", new Version(1, 0, int.MaxValue, int.MaxValue), UnstableMessage),
  127. // Ed Harvey's Threshold effect, v1.0, uses a timer in its config dialog that NullRef's on us
  128. Quadruple.Create("EdHarvey.Edfects.Effects", "ThresholdEffect", new Version (1, 0, int.MaxValue, int.MaxValue), UnstableMessage),
  129. // BoltBait's InkSketch, which is now built-in to Paint.NET
  130. Quadruple.Create("InkSketch", "EffectPlugin", new Version(1, 0, int.MaxValue, int.MaxValue), BuiltInMessage),
  131. // Portrait, which is now built-in to Paint.NET ("Soften Portrait")
  132. Quadruple.Create("PortraitEffect", "EffectPlugin", new Version(1, 0, int.MaxValue, int.MaxValue), BuiltInMessage),
  133. // Reduce Noice, which is now built-in to Paint.NET
  134. Quadruple.Create("HistogramEffects", "ReduceNoiseEffect", new Version (1, 1, 0, 0), BuiltInMessage),
  135. // Fragment, which is now built-in
  136. Quadruple.Create("EdHarvey.Edfects.Effects", "FragmentEffect", new Version(3, 20, int.MaxValue, int.MaxValue), BuiltInMessage),
  137. // Posterize, which is now built-in
  138. Quadruple.Create("EdHarvey.Edfects.Effects", "PosterizeEffect", new Version(3, 31, int.MaxValue, int.MaxValue), BuiltInMessage),
  139. // Some of pyrochild's effects were using some code in PaintDotNet.exe that is now marked internal.
  140. // For some reason it is not caught by the plugin crash dialog in non-Debug builds.
  141. Quadruple.Create("pyrochild.effects.splatter", "Splatter", new Version(1, 0, int.MaxValue, int.MaxValue), UnstableMessage),
  142. Quadruple.Create("zachwalker.CurvesPlus", "CurvesPlus", new Version(2, 4, int.MaxValue, int.MaxValue), UnstableMessage),
  143. // Older versions of these were accessing code in paintdotnet.exe, or were using the EventHandler<T> that was moved
  144. Quadruple.Create("pyrochild.effects.gradientmapping", "GradientMapping", new Version(2, 1, int.MaxValue, int.MaxValue), UnstableMessage),
  145. // GREYCstoration, has known stability issues, and is also superceded by the built-in "Reduce Noise" anyway
  146. Quadruple.Create("GREYCstoration", "BaseEffect", new Version(0, 9, int.MaxValue, int.MaxValue), UnstableMessage),
  147. Quadruple.Create("GREYCstoration", "RestoreEffect", new Version(0, 9, int.MaxValue, int.MaxValue), UnstableMessage)
  148. };
  149. private static Dictionary<string, List<Triple<string, Version, string>>> _indexedBlockedEffects;
  150. private static Exception IsBannedEffect(Type effectType)
  151. {
  152. // Never block a built-in effect.
  153. if (effectType.Assembly == typeof(Effect).Assembly)
  154. {
  155. return null;
  156. }
  157. // Make an index of that big Quadruple[] up there. Otherwise we may never scale well because
  158. // we'll end up with the performance proportional to installed plugins * blocked plugins.
  159. if (_indexedBlockedEffects == null)
  160. {
  161. lock (BlockedEffects)
  162. {
  163. _indexedBlockedEffects = new Dictionary<string, List<Triple<string, Version, string>>>(StringComparer.InvariantCultureIgnoreCase);
  164. foreach (var blockedEffect in BlockedEffects)
  165. {
  166. string blockedEffectNamespace = blockedEffect.First;
  167. var blockedEffectDetails = blockedEffect.GetTriple234();
  168. List<Triple<string, Version, string>> blockedEffectDetailsSet;
  169. if (!_indexedBlockedEffects.TryGetValue(blockedEffectNamespace, out blockedEffectDetailsSet))
  170. {
  171. blockedEffectDetailsSet = new List<Triple<string, Version, string>>();
  172. _indexedBlockedEffects.Add(blockedEffectNamespace, blockedEffectDetailsSet);
  173. }
  174. blockedEffectDetailsSet.Add(blockedEffectDetails);
  175. }
  176. }
  177. }
  178. // Gather the effect's details.
  179. Version assemblyVersion = GetAssemblyVersionFromType(effectType);
  180. string effectNamespace = effectType.Namespace;
  181. string effectName = effectType.Name;
  182. #if DEBUG
  183. /*
  184. // Makes it easy to copy+paste into the big Quadruple[] up there
  185. SystemLayer.Tracing.Ping(string.Format(
  186. "IsBannedEffect -- Quadruple.Create(\"{0}\", \"{1}\", new Version({2}, {3}, {4}, {5}), ...)",
  187. effectNamespace,
  188. effectName,
  189. assemblyVersion.Major,
  190. assemblyVersion.Minor,
  191. assemblyVersion.Build,
  192. assemblyVersion.Revision));
  193. * */
  194. #endif
  195. // Block list #1
  196. if (effectNamespace != null)
  197. {
  198. List<Triple<string, Version, string>> blockedEffectDetailsList2;
  199. if (_indexedBlockedEffects.TryGetValue(effectNamespace, out blockedEffectDetailsList2))
  200. {
  201. foreach (var blockedEffectDetails2 in
  202. blockedEffectDetailsList2.Where(blockedEffectDetails2 => 0 == string.Compare(effectName, blockedEffectDetails2.First, StringComparison.InvariantCultureIgnoreCase) && assemblyVersion <= blockedEffectDetails2.Second))
  203. {
  204. return new BlockedPluginException(blockedEffectDetails2.Third);
  205. }
  206. }
  207. // Block list #2 -- Block based on Guid and namespace
  208. if (CheckForAnyGuidOnType(effectType, DeprecatedEffectGuids) &&
  209. (0 == string.Compare(effectNamespace, "GlowEffect", StringComparison.InvariantCultureIgnoreCase) ||
  210. 0 == string.Compare(effectNamespace, "DistortionEffects", StringComparison.InvariantCultureIgnoreCase)))
  211. {
  212. return new BlockedPluginException();
  213. }
  214. }
  215. return null;
  216. }
  217. private static List<Type> GetEffectsFromAssemblies(IEnumerable<Assembly> assemblies, ICollection<Triple<Assembly, Type, Exception>> errorsResult)
  218. {
  219. var effects = new List<Type>();
  220. foreach (Assembly assembly in assemblies)
  221. {
  222. GetEffectsFromAssembly(assembly, effects, errorsResult);
  223. }
  224. var removeUs = new List<Type>();
  225. foreach (Type effectType in effects)
  226. {
  227. Exception bannedEx = IsBannedEffect(effectType);
  228. if (bannedEx == null) continue;
  229. removeUs.Add(effectType);
  230. errorsResult.Add(Triple.Create(effectType.Assembly, effectType, bannedEx));
  231. }
  232. foreach (Type removeThisType in removeUs)
  233. {
  234. effects.Remove(removeThisType);
  235. }
  236. return effects;
  237. }
  238. private static void GetEffectsFromAssembly(Assembly assembly, ICollection<Type> effectsResult, ICollection<Triple<Assembly, Type, Exception>> errorsResult)
  239. {
  240. try
  241. {
  242. IEnumerable<Type> types = GetTypesFromAssembly(assembly, errorsResult);
  243. foreach (Type type in
  244. types.Where(type => type.IsSubclassOf(typeof (Effect)) && !type.IsAbstract && !Utility.IsObsolete(type, false)))
  245. {
  246. effectsResult.Add(type);
  247. }
  248. }
  249. catch (ReflectionTypeLoadException)
  250. {
  251. }
  252. }
  253. private static IEnumerable<Type> GetTypesFromAssembly(Assembly assembly, ICollection<Triple<Assembly, Type, Exception>> errorsResult)
  254. {
  255. Type[] types;
  256. try
  257. {
  258. types = assembly.GetTypes();
  259. }
  260. catch (ReflectionTypeLoadException rex)
  261. {
  262. Type[] rexTypes = rex.Types;
  263. foreach (Exception loadEx in rex.LoaderExceptions)
  264. {
  265. // Set Type to null, and the error dialog will look at the exception, see it is a TypeLoadException,
  266. // and use the TypeName property from there.
  267. errorsResult.Add(Triple.Create<Assembly, Type, Exception>(assembly, null, loadEx));
  268. }
  269. types = rexTypes.Where(rexType => rexType != null).ToArray();
  270. }
  271. return types;
  272. }
  273. }
  274. }