From 733dbf9bd7817c95fadae4535d4b9e92a42a7f4b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 25 Mar 2020 15:04:44 -0600 Subject: [PATCH] Moves all plugin logic to new PluginLoader static class --- PepperDashEssentials/ControlSystem.cs | 153 +--------- PepperDashEssentials/HttpApiHandler.cs | 266 ---------------- .../PepperDashEssentials.csproj | 2 +- .../PluginLoading/PluginLoading.cs | 287 ++++++++++++++++++ .../PepperDashEssentialsBase/Global/Global.cs | 9 - 5 files changed, 295 insertions(+), 422 deletions(-) delete mode 100644 PepperDashEssentials/HttpApiHandler.cs create mode 100644 PepperDashEssentials/PluginLoading/PluginLoading.cs diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index a9b16686..46ac77f2 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -25,7 +25,6 @@ namespace PepperDash.Essentials { HttpLogoServer LogoServer; - List LoadedAssemblies = new List(); public ControlSystem() : base() @@ -48,6 +47,8 @@ namespace PepperDash.Essentials ConsoleAccessLevelEnum.AccessOperator); } + CrestronConsole.AddNewConsoleCommand(PluginLoader.ReportAssemblyVersions, "reportversions", "Reports the versions of the loaded assemblies", ConsoleAccessLevelEnum.AccessOperator); + // CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => { @@ -80,6 +81,8 @@ namespace PepperDash.Essentials GoWithLoad(); } + + /// /// Determines if the program is running on a processor (appliance) or server (VC-4). /// @@ -160,20 +163,13 @@ namespace PepperDash.Essentials Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration"); - // Get the loaded assembly filenames - var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix); - var assemblyFiles = appDi.GetFiles("*.dll"); - - foreach (var file in assemblyFiles) - { - LoadedAssemblies.Add(file.Name); - } + PluginLoader.AddProgramAssemblies(); var filesReady = SetupFilesystem(); if (filesReady) { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Checking for plugins"); - LoadPlugins(); + PluginLoader.LoadPlugins(); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config..."); if (!ConfigReader.LoadConfig2()) @@ -208,142 +204,7 @@ namespace PepperDash.Essentials } - /// - /// Loads plugins - /// - void LoadPlugins() - { - var dir = Global.FilePathPrefix + "plugins"; - if (Directory.Exists(dir)) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Plugins directory found, checking for factory plugins"); - var di = new DirectoryInfo(dir); - var zFiles = di.GetFiles("*.cplz"); - foreach (var fi in zFiles) - { - Debug.Console(0, "Found cplz: {0}. Unzipping into plugins directory", fi.Name); - var result = CrestronZIP.Unzip(fi.FullName, di.FullName); - Debug.Console(0, "UnZip Result: {0}", result.ToString()); - fi.Delete(); - } - var files = di.GetFiles("*.dll"); - Dictionary assyList = new Dictionary(); - - foreach (FileInfo fi in files) - { - if (!CheckIfAssemblyExists(fi.Name)) - { - try - { - string assyVersion = string.Empty; - var assy = Assembly.LoadFrom(fi.FullName); - var ver = assy.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); - if (ver != null) - { - AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute; - assyVersion = verAttribute.InformationalVersion; - } - else - - - assyList.Add(fi.FullName, assy); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loaded plugin file '{0}', version {1}", fi.FullName, assyVersion); - } - catch - { - Debug.Console(2, "Assembly {0} is not a custom assembly", fi.FullName); - continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here - } - } - else - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Skipping plugin: {0}. There is already an assembly with that name loaded.", fi.Name); - } - foreach (var assy in assyList) - { - // iteratate this assembly's classes, looking for "LoadPlugin()" methods - try - { - var types = assy.Value.GetTypes(); - foreach (var type in types) - { - try - { - var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static); - var loadPlugin = methods.FirstOrDefault(m => m.Name.Equals("LoadPlugin")); - if (loadPlugin != null) - { - Debug.Console(2, "LoadPlugin method found in {0}", type.Name); - - var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static); - - var minimumVersion = fields.FirstOrDefault(p => p.Name.Equals("MinimumEssentialsFrameworkVersion")); - if (minimumVersion != null) - { - Debug.Console(2, "MinimumEssentialsFrameworkVersion found"); - - var minimumVersionString = minimumVersion.GetValue(null) as string; - - if (!string.IsNullOrEmpty(minimumVersionString)) - { - var passed = Global.IsRunningMinimumVersionOrHigher(minimumVersionString); - - if (!passed) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", minimumVersionString); - continue; - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Passed plugin passed dependency check (required version {0})", minimumVersionString); - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion found but not set. Loading plugin, but your mileage may vary."); - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion not found. Loading plugin, but your mileage may vary."); - } - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding plugin: {0}", assy.Key); - loadPlugin.Invoke(null, null); - } - } - catch - { - Debug.Console(2, "Load Plugin not found. {0} is not a plugin assembly", assy.Value.FullName); - continue; - } - - } - } - catch - { - Debug.Console(2, "Assembly {0} is not a custom assembly. Types cannot be loaded.", assy.Value.FullName); - continue; - } - } - // plugin dll will be loaded. Any classes in plugin should have a static constructor - // that registers that class with the Core.DeviceFactory - } - } - - /// - /// Checks if the filename matches an already loaded assembly file's name - /// - /// - /// True if file already matches loaded assembly file. - bool CheckIfAssemblyExists(string filename) - { - var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.Equals(filename)); - - if (loadedAssembly != null) - return true; - else - return false; - } + /// /// Verifies filesystem is set up. IR, SGD, and programX folders diff --git a/PepperDashEssentials/HttpApiHandler.cs b/PepperDashEssentials/HttpApiHandler.cs deleted file mode 100644 index 0a757999..00000000 --- a/PepperDashEssentials/HttpApiHandler.cs +++ /dev/null @@ -1,266 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using Crestron.SimplSharp; -//using Crestron.SimplSharp.CrestronIO; -//using Crestron.SimplSharp.Net.Http; - -//using Newtonsoft.Json; -//using Newtonsoft.Json.Linq; - -//using PepperDash.Essentials.Core; -//using PepperDash.Essentials.Core.Http; -//using PepperDash.Core; - -//namespace PepperDash.Essentials -//{ -// public class EssentialsHttpApiHandler -// { -// string ConfigPath; -// string PresetsPathPrefix; -// EssentialsHttpServer Server; - -// /// -// /// -// /// -// /// HTTP server to attach to -// /// The full path to configuration file -// /// The folder prefix for the presets path, eq "\HTML\presets\" -// public EssentialsHttpApiHandler(EssentialsHttpServer server, string configPath, string presetsPathPrefix) -// { -// if (server == null) throw new ArgumentNullException("server"); -// Server = server; -// ConfigPath = configPath; -// PresetsPathPrefix = presetsPathPrefix; -// server.ApiRequest += Server_ApiRequest; -// } - - -// void Server_ApiRequest(object sender, Crestron.SimplSharp.Net.Http.OnHttpRequestArgs args) -// { -// try -// { -// var path = args.Request.Path.ToLower(); - -// if (path == "/api/config") -// HandleApiConfig(args); -// else if (path.StartsWith("/api/presetslist/")) -// HandleApiPresetsList(args); -// else if (path == "/api/presetslists") -// HandleApiGetPresetsLists(args.Request, args.Response); -// else -// { -// args.Response.Code = 404; -// return; -// } -// args.Response.Header.SetHeaderValue("Access-Control-Allow-Origin", "*"); -// args.Response.Header.SetHeaderValue("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS"); -// } -// catch (Exception e) -// { -// Debug.Console(1, "Uncaught HTTP server error: \n{0}", e); -// args.Response.Code = 500; -// } -// } - -// /// -// /// GET will return the running configuration. POST will attempt to take in a new config -// /// and restart the program. -// /// -// void HandleApiConfig(OnHttpRequestArgs args) -// { -// var request = args.Request; -// if (request.Header.RequestType == "GET") -// { -// if (File.Exists(ConfigPath)) -// { -// Debug.Console(2, "Sending config:{0}", ConfigPath); -// args.Response.Header.ContentType = EssentialsHttpServer.GetContentType(new FileInfo(ConfigPath).Extension); -// args.Response.ContentStream = new FileStream(ConfigPath, FileMode.Open, FileAccess.Read); -// } -// } -// else if (request.Header.RequestType == "POST") -// { -// Debug.Console(2, "Post type: '{0}'", request.Header.ContentType); - -// // Make sure we're receiving at least good json -// Debug.Console(1, "Receving new config"); -// if (GetContentStringJson(args) == null) -// return; - -// //---------------------------- try to move these into common method -// // Move current file aside -// var bakPath = ConfigPath + ".bak"; -// if (File.Exists(bakPath)) -// File.Delete(bakPath); -// File.Move(ConfigPath, bakPath); - -// // Write the file -// using (FileStream fs = File.Open(ConfigPath, FileMode.OpenOrCreate)) -// using (StreamWriter sw = new StreamWriter(fs)) -// { -// try -// { -// sw.Write(args.Request.ContentString); -// } -// catch (Exception e) -// { -// string err = string.Format("Error writing received config file:\r{0}", e); -// CrestronConsole.PrintLine(err); -// ErrorLog.Warn(err); -// // Put file back -// File.Move(ConfigPath + ".bak", ConfigPath); -// args.Response.Code = 500; -// return; -// } -// } - -// // If client says "yeah, restart" and has a good token -// // Restart program -// string consoleResponse = null; -// var restart = CrestronConsole.SendControlSystemCommand("progreset -p:" + -// InitialParametersClass.ApplicationNumber, ref consoleResponse); -// if (!restart) Debug.Console(0, "CAN'T DO THAT YO: {0}", consoleResponse); -// } -// } - -// void HandleApiPresetsList(OnHttpRequestArgs args) -// { -// var listPath = PresetsPathPrefix + args.Request.Path.Remove(0, 17); -// Debug.Console(2, "Checking for preset list '{0}'", listPath); - -// if (args.Request.Header.RequestType == "GET") -// { -// if (File.Exists(listPath)) -// { -// Debug.Console(2, "Sending presets file:{0}", listPath); -// args.Response.Header.ContentType = EssentialsHttpServer.GetContentType(new FileInfo(listPath).Extension); -// args.Response.ContentStream = new FileStream(listPath, FileMode.Open, FileAccess.Read); -// } -// } -// else if (args.Request.Header.RequestType == "POST") -// { -// // Make sure we're receiving at least good json -// Debug.Console(1, "Receving new presets"); -// if (GetContentStringJson(args) == null) -// return; - -// //---------------------------- try to move these into common method -// // Move current file aside -// var bakPath = listPath + ".new"; -// Debug.Console(2, "Moving presets file to {0}", bakPath); -// if(File.Exists(bakPath)) -// File.Delete(bakPath); -// File.Move(listPath, bakPath); - -// Debug.Console(2, "Writing new file"); -// // Write the file -// using (FileStream fs = File.OpenWrite(listPath)) -// using (StreamWriter sw = new StreamWriter(fs)) -// { -// try -// { -// Debug.Console(2, "Writing {1}, {0} bytes", args.Request.ContentString.Length, listPath); -// sw.Write(args.Request.ContentString); -// } -// catch (Exception e) -// { -// string err = string.Format("Error writing received presets file:\r{0}", e); -// CrestronConsole.PrintLine(err); -// ErrorLog.Warn(err); -// // Put file back -// File.Move(listPath + ".bak", listPath); -// args.Response.Code = 500; -// return; -// } -// } -// } -// } - - -// void HandleApiGetPresetsLists(HttpServerRequest request, HttpServerResponse response) -// { -// if (request.Header.RequestType != "GET") -// { -// response.Code = 404; // This should be a 405 with an allow header -// return; -// } - -// if (Directory.Exists(PresetsPathPrefix)) -// { -// //CrestronConsole.PrintLine("Parsing presets directory"); -// List files = Directory.GetFiles(PresetsPathPrefix, "*.json") -// .ToList().Select(f => Path.GetFileName(f)).ToList(); -// if (files.Count > 0) -// files.Sort(); -// var json = JsonConvert.SerializeObject(files); -// response.Header.ContentType = "application/json"; -// response.ContentString = json; -// } - -// // //CrestronConsole.PrintLine("Found {0} files", files.Count); -// // JObject jo = new JObject(); -// // JArray ja = new JArray(); - -// // foreach (var filename in files) -// // { -// // try -// // { -// // using (StreamReader sr = new StreamReader(filename)) -// // { -// // JObject tempJo = JObject.Parse(sr.ReadToEnd()); -// // if (tempJo.Value("content").Equals("presetsList")) -// // { -// // var jItem = new JObject(); // make a new object -// // jItem.Add("Name", tempJo["name"]); -// // jItem.Add("File", filename); -// // jItem.Add("Url", Uri.EscapeUriString(new Uri( -// // filename.Replace("\\html", "") -// // .Replace("\\HTML", "") -// // .Replace('\\', '/'), UriKind.Relative).ToString())); -// // ja.Add(jItem); // add to array -// // } -// // else -// // CrestronConsole.PrintLine("Cannot use presets file '{0}'", filename); -// // } -// // } -// // catch -// // { -// // // ignore failures - maybe delete them -// // CrestronConsole.PrintLine("Unable to read presets file '{0}'", filename); -// // } -// // } -// // jo.Add("PresetChannelLists", ja); -// // //CrestronConsole.PrintLine(jo.ToString()); -// // response.Header.ContentType = "application/json"; -// // response.ContentString = jo.ToString(); -// //} -// //else -// // CrestronConsole.PrintLine("No presets files in directory"); -// } - -// /// -// /// Simply does what it says -// /// -// JObject GetContentStringJson(OnHttpRequestArgs args) -// { -// //var content = args.Request.ContentString; -// //Debug.Console(1, "{0}", content); - -// try -// { -// // just see if it parses properly -// return JObject.Parse(args.Request.ContentString); -// } -// catch (Exception e) -// { -// string err = string.Format("JSON Error reading config file:\r{0}", e); -// CrestronConsole.PrintLine(err); -// ErrorLog.Warn(err); -// args.Response.Code = 400; // Bad request -// return null; -// } -// } -// } -//} \ No newline at end of file diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index 612bdf10..0bd07ecf 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -166,7 +166,7 @@ - + diff --git a/PepperDashEssentials/PluginLoading/PluginLoading.cs b/PepperDashEssentials/PluginLoading/PluginLoading.cs new file mode 100644 index 00000000..4d501545 --- /dev/null +++ b/PepperDashEssentials/PluginLoading/PluginLoading.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Reflection; + +using PepperDash.Core; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials +{ + /// + /// Deals with loading plugins at runtime + /// + public static class PluginLoader + { + /// + /// The complete list of loaded assemblies. Includes Essentials Framework assemblies and plugins + /// + public static List LoadedAssemblies { get; private set; } + + /// + /// The list of assemblies loaded from the plugins folder + /// + static List LoadedPluginFolderAssemblies; + + static PluginLoader() + { + LoadedAssemblies = new List(); + LoadedPluginFolderAssemblies = new List(); + } + + /// + /// Retrieves all the loaded assemblies from the program directory + /// + public static void AddProgramAssemblies() + { + // Get the loaded assembly filenames + var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix); + var assemblyFiles = appDi.GetFiles("*.dll"); + + foreach (var file in assemblyFiles) + { + try + { + LoadAssembly(file.FullName); + } + catch + { + Debug.Console(2, "Assembly {0} is not a custom assembly", file.FullName); + } + } + } + + /// + /// Loads an assembly via Reflection and adds it to the list of loaded assemblies + /// + /// + static LoadedAssembly LoadAssembly(string fileName) + { + if (!CheckIfAssemblyExists(fileName)) + { + + var assembly = Assembly.LoadFrom(fileName); + if (assembly != null) + { + var assyVersion = GetAssemblyVersion(assembly); + + var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly); + LoadedAssemblies.Add(loadedAssembly); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loaded plugin file '{0}', version {1}", loadedAssembly.FileName, loadedAssembly.Version); + return loadedAssembly; + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to load assembly: '{0}'", fileName); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Skipping assembly: {0}. There is already an assembly with that name loaded.", fileName); + } + + return null; + + } + + /// + /// Attempts to get the assembly informational version and if not possible gets the version + /// + /// + /// + static string GetAssemblyVersion(Assembly assembly) + { + var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); + if (ver != null) + { + // Get the AssemblyInformationalVersion + AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute; + return verAttribute.InformationalVersion; + } + else + { + // Get the AssemblyVersion + var version = assembly.GetName().Version; + var verStr = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision); + return verStr; + } + } + + /// + /// Checks if the filename matches an already loaded assembly file's name + /// + /// + /// True if file already matches loaded assembly file. + public static bool CheckIfAssemblyExists(string filename) + { + Debug.Console(2, "Checking if assembly: {0} is loaded...", filename); + var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.FileName.Equals(filename)); + + if (loadedAssembly != null) + { + return true; + } + else + { + return false; + } + } + + /// + /// Used by console command to report the currently loaded assemblies and versions + /// + /// + public static void ReportAssemblyVersions(string command) + { + Debug.Console(0, "Loaded Assemblies:"); + foreach (var assembly in LoadedAssemblies) + { + Debug.Console(0, "{0} Version: {1}", assembly.FileName, assembly.Version); + } + } + + /// + /// Loads plugins + /// + public static void LoadPlugins() + { + var dir = Global.FilePathPrefix + "plugins"; + var tempDir = dir + Global.DirectorySeparator + "temp"; + + if (Directory.Exists(dir)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Plugins directory found, checking for factory plugins"); + var di = new DirectoryInfo(dir); + var zFiles = di.GetFiles("*.cplz"); + + foreach (var fi in zFiles) + { + Directory.CreateDirectory(tempDir); + var tempDi = new DirectoryInfo(tempDir); + + Debug.Console(0, "Found cplz: {0}. Unzipping into temp plugins directory", fi.Name); + var result = CrestronZIP.Unzip(fi.FullName, tempDi.FullName); + Debug.Console(0, "UnZip Result: {0}", result.ToString()); + + var files = tempDi.GetFiles("*.dll"); + + foreach (FileInfo file in files) + { + try + { + var pluginAssembly = LoadAssembly(fi.FullName); + + if (pluginAssembly != null) + { + LoadedPluginFolderAssemblies.Add(pluginAssembly); + } + } + catch + { + Debug.Console(2, "Assembly {0} is not a custom assembly", file.FullName); + continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here + } + + } + + // Delete the .cplz and the temp directory + fi.Delete(); + Directory.Delete(tempDir); + } + + // Iterate the loaded assemblies and try to call the LoadPlugin method + foreach (var loadedAssembly in LoadedPluginFolderAssemblies) + { + // iteratate this assembly's classes, looking for "LoadPlugin()" methods + try + { + var assy = loadedAssembly.Assembly; + var types = assy.GetTypes(); + foreach (var type in types) + { + try + { + var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static); + var loadPlugin = methods.FirstOrDefault(m => m.Name.Equals("LoadPlugin")); + if (loadPlugin != null) + { + Debug.Console(2, "LoadPlugin method found in {0}", type.Name); + + var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static); + + var minimumVersion = fields.FirstOrDefault(p => p.Name.Equals("MinimumEssentialsFrameworkVersion")); + if (minimumVersion != null) + { + Debug.Console(2, "MinimumEssentialsFrameworkVersion found"); + + var minimumVersionString = minimumVersion.GetValue(null) as string; + + if (!string.IsNullOrEmpty(minimumVersionString)) + { + var passed = Global.IsRunningMinimumVersionOrHigher(minimumVersionString); + + if (!passed) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", minimumVersionString); + continue; + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Passed plugin passed dependency check (required version {0})", minimumVersionString); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion found but not set. Loading plugin, but your mileage may vary."); + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Warning, "MinimumEssentialsFrameworkVersion not found. Loading plugin, but your mileage may vary."); + } + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Adding plugin: {0}", loadedAssembly.FileName); + loadPlugin.Invoke(null, null); + } + } + catch + { + Debug.Console(2, "Load Plugin not found. {0} is not a plugin assembly", loadedAssembly.FileName); + continue; + } + + } + } + catch + { + Debug.Console(2, "Assembly {0} is not a custom assembly. Types cannot be loaded.", loadedAssembly.FileName); + continue; + } + } + // plugin dll will be loaded. Any classes in plugin should have a static constructor + // that registers that class with the Core.DeviceFactory + } + } + + } + + /// + /// Represents an assembly loaded at runtime and it's associated metadata + /// + public class LoadedAssembly + { + public string FileName { get; private set; } + public string Version { get; private set; } + public Assembly Assembly { get; private set; } + + public LoadedAssembly(string fileName, string version, Assembly assembly) + { + FileName = fileName; + Version = version; + Assembly = assembly; + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs index 251ecf44..4ef68066 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Global.cs @@ -62,15 +62,6 @@ namespace PepperDash.Essentials.Core FilePathPrefix = prefix; } - /// - /// Sets the file path prefix - /// - /// - public static void SetApplicationDirectoryPathPrefix(string prefix) - { - ApplicationDirectoryPathPrefix = prefix; - } - static string _AssemblyVersion; ///