/// <summary> /// 获取描述文件 /// </summary> /// <param name="pluginFolder">插件文件夹</param> /// <returns>原始和解析的描述文件</returns> private static IEnumerable <KeyValuePair <FileInfo, PluginDescriptor> > GetDescriptionFilesAndDescriptors(DirectoryInfo pluginFolder) { if (pluginFolder == null) { throw new ArgumentNullException("pluginFolder"); } //create list (<file info, parsed plugin descritor>) var result = new List <KeyValuePair <FileInfo, PluginDescriptor> >(); //add display order and path to list foreach (var descriptionFile in pluginFolder.GetFiles("Description.txt", SearchOption.AllDirectories)) { if (!IsPackagePluginFolder(descriptionFile.Directory)) { continue; } //parse file var pluginDescriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName); //populate list result.Add(new KeyValuePair <FileInfo, PluginDescriptor>(descriptionFile, pluginDescriptor)); } //sort list by display order. NOTE: Lowest DisplayOrder will be first i.e 0 , 1, 1, 1, 5, 10 //it's required: http://www.nopcommerce.com/boards/t/17455/load-plugins-based-on-their-displayorder-on-startup.aspx result.Sort((firstPair, nextPair) => firstPair.Value.DisplayOrder.CompareTo(nextPair.Value.DisplayOrder)); return(result); }
/// <summary> /// Initialize /// </summary> public static void Initialize() { using (new WriteLockDisposable(Locker)) { // TODO: Add verbose exception handling / raising here since this is happening on app startup and could // prevent app from starting altogether var pluginFolder = new DirectoryInfo(HostingEnvironment.MapPath(_pluginsPath)); _shadowCopyFolder = new DirectoryInfo(HostingEnvironment.MapPath(_shadowCopyPath)); var referencedPlugins = new List <PluginDescriptor>(); _clearShadowDirectoryOnStartup = !String.IsNullOrEmpty(ConfigurationManager.AppSettings["ClearPluginsShadowDirectoryOnStartup"]) && Convert.ToBoolean(ConfigurationManager.AppSettings["ClearPluginsShadowDirectoryOnStartup"]); try { var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(GetInstalledPluginsFilePath()); Debug.WriteLine("Creating shadow copy folder and querying for dlls"); //ensure folders are created Directory.CreateDirectory(pluginFolder.FullName); Directory.CreateDirectory(_shadowCopyFolder.FullName); //get list of all files in bin var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories); if (_clearShadowDirectoryOnStartup) { //clear out shadow copied plugins foreach (var f in binFiles) { Debug.WriteLine("Deleting " + f.Name); try { File.Delete(f.FullName); } catch (Exception exc) { Debug.WriteLine("Error deleting file " + f.Name + ". Exception: " + exc); } } } //load description files foreach (var descriptionFile in pluginFolder.GetFiles("Description.txt", SearchOption.AllDirectories)) { try { //parse file var description = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName); //ensure that version of plugin is valid if (!description.SupportedVersions.Contains(NopVersion.CurrentVersion, StringComparer.InvariantCultureIgnoreCase)) { continue; } //some validation if (String.IsNullOrWhiteSpace(description.SystemName)) { throw new Exception(string.Format("A plugin '{0}' has no system name. Try assigning the plugin a unique name and recompiling.", descriptionFile.FullName)); } if (referencedPlugins.Contains(description)) { throw new Exception(string.Format("A plugin with '{0}' system name is already defined", description.SystemName)); } //set 'Installed' property description.Installed = installedPluginSystemNames .ToList() .Where(x => x.Equals(description.SystemName, StringComparison.InvariantCultureIgnoreCase)) .FirstOrDefault() != null; //get list of all DLLs in plugins (not in bin!) var pluginFiles = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories) //just make sure we're not registering shadow copied plugins .Where(x => !binFiles.Select(q => q.FullName).Contains(x.FullName)) .Where(x => IsPackagePluginFolder(x.Directory)) .ToList(); //other plugin description info var mainPluginFile = pluginFiles.Where(x => x.Name.Equals(description.PluginFileName, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); description.OriginalAssemblyFile = mainPluginFile; //shadow copy main pugin file description.ReferencedAssembly = PerformFileDeploy(mainPluginFile); //load all other referenced assemblies now foreach (var plugin in pluginFiles .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.InvariantCultureIgnoreCase)) .Where(x => !IsAlreadyLoaded(x))) { PerformFileDeploy(plugin); } //init plugin type (only one plugin per assembly is allowed) foreach (var t in description.ReferencedAssembly.GetTypes()) { if (typeof(IPlugin).IsAssignableFrom(t)) { if (!t.IsInterface) { if (t.IsClass && !t.IsAbstract) { description.PluginType = t; break; } } } } referencedPlugins.Add(description); } catch (ReflectionTypeLoadException ex) { var msg = string.Empty; foreach (var e in ex.LoaderExceptions) { msg += e.Message + Environment.NewLine; } msg += descriptionFile.DirectoryName + Environment.NewLine; var fail = new Exception(msg, ex); Debug.WriteLine(fail.Message, fail); throw fail; } //catch (Exception ex) //{ // var fail = new Exception("Could not initialise plugin folder", ex); // Debug.WriteLine(fail.Message); // throw fail; //} } } catch (Exception ex) { var msg = string.Empty; for (var e = ex; e != null; e = e.InnerException) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); Debug.WriteLine(fail.Message, fail); throw fail; } ReferencedPlugins = referencedPlugins; } }