/// <summary> /// Compiles the module. /// </summary> /// <param name="codeTexts">The code texts.</param> /// <param name="module">The module.</param> /// <param name="references">The references.</param> /// <param name="objParams">The object parameters.</param> /// <param name="pluginSystemVersion">The plugin system version.</param> /// <returns></returns> static public PluginCommon CompileModule(string[] codeTexts, string module, string[] references, object objParams, string pluginSystemVersion) { string compiledName = AssemblyUtils.CompiledPluginName(module, pluginSystemVersion, string.Concat("-", AssemblyUtils.TimeStampForFile(DateTime.Now), ".dll")); //string compiledName = CompiledPluginName(module, pluginSystemVersion, // string.Concat("-", Path.GetRandomFileName().Substring(0, 8), ".dll")); CompilerParameters cp = new CompilerParameters(new[] { "System.Windows.Forms.dll", "System.dll", "System.Data.dll", "System.Drawing.dll", "System.Xml.dll" }, chachePath + compiledName, #if DEBUG true); #else false); #endif cp.TempFiles = new TempFileCollection(chachePath, false); //set directory cp.ReferencedAssemblies.Add(System.Windows.Forms.Application.ExecutablePath); cp.GenerateExecutable = false; cp.GenerateInMemory = false; cp.CompilerOptions = "/optimize"; cp.WarningLevel = 4; //to do not consider C00618 warning (obsolete PluginBaseV0_0 class) cp.MainClass = "Gear.PluginSupport." + module; //traverse list adding not null nor empty texts foreach (string s in references) { if (!string.IsNullOrEmpty(s)) { cp.ReferencedAssemblies.Add(s); } } CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(); try { if (!Directory.Exists(chachePath)) { Directory.CreateDirectory(chachePath); } else { try { Directory.Delete(chachePath, true); Directory.CreateDirectory(chachePath); } catch (Exception e) { CompilerError c = new CompilerError(string.Empty, 0, 0, "Pre-build", e.Message); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } } //write the codes to compile later string[] sourceFiles = new string[codeTexts.Length]; for (int i = 0; i < codeTexts.Length; i++) { sourceFiles[i] = string.Format(chachePath + "{0}-{1}.cs", AssemblyUtils.CompiledPluginName(module, pluginSystemVersion, string.Empty), i); File.WriteAllText(sourceFiles[i], codeTexts[i]); } AppDomainSetup adSetup = new AppDomainSetup(); adSetup.ApplicationBase = System.Environment.CurrentDirectory; adSetup.ApplicationName = "Plugin space App"; adSetup.ShadowCopyFiles = "true"; adSetup.CachePath = chachePath; AppDomain pluginDomain = AppDomain.CreateDomain("pluginDomain", null, adSetup); //compile the assembly CompilerResults results = provider.CompileAssemblyFromFile(cp, sourceFiles); if (results.Errors.HasErrors | results.Errors.HasWarnings) { m_Errors = results.Errors; return(null); } string compiledAssembly = results.CompiledAssembly.FullName; //PrintAssembliesLoaded(AppDomain.CurrentDomain, results.CompiledAssembly, true); //byte[] rawAssembly = loadFile(@".\cache\" + compiledName); //Assembly assemblyPlugin = pluginDomain.Load(rawAssembly, null); //PrintAssembliesLoaded(pluginDomain, assemblyPlugin, true); // TODO ASB: see links to help about running in a different AppDomain //http://stackoverflow.com/questions/5380246/loading-services-from-other-dll-and-run-them-isolated/5380317#5380317 //http://stackoverflow.com/questions/599731/use-the-serializable-attribute-or-subclassing-from-marshalbyrefobject //explore how to use MarshalByRefObject: http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html //http://stackoverflow.com/questions/1687245/use-appdomain-to-load-unload-external-assemblies object target = pluginDomain.CreateInstanceAndUnwrap( compiledAssembly, //string assemblyName module, //string typeName false, //bool ignoreCase BindingFlags.Public | BindingFlags.Instance, //BindingFlags bindingAttr null, //Binder binder (objParams != null) ? //object[] args new object[] { objParams } : null, null, //CultureInfo culture null); //object[] activationAttributes PrintAssembliesLoaded(pluginDomain, results.CompiledAssembly, false); if (target == null) { CompilerError c = new CompilerError(string.Empty, 0, 0, "CS0103", "The name '" + module + "' does not exist in the current context." + " Does the class name is the same that is declared in c# code?"); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } else if (!PluginSystem.InstanceOneOfValidClasses(target)) { CompilerError c = new CompilerError(string.Empty, 0, 0, "CS0029", "Cannot implicitly convert type '" + target.GetType().FullName + "' to '" + PluginSystem.GetPluginBaseType(pluginSystemVersion).FullName + "'"); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } m_Errors = null; return((PluginCommon)target); } catch (Exception e) { CompilerError c = new CompilerError(string.Empty, 0, 0, "Runtime", string.Format("Plugin '{0}' - {1}", module, e.Message)); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } }
/// @brief Dynamic compiling & loading for a plugin. /// @details Try to dynamically compile a module for the plugin, based on supplied /// C# code and other C# modules referenced, using Reflexion. If the compiling fails, /// it gives a list of errors, intended to be showed in the plugin view. /// @param[in] codeTexts C# Source code based on PluginBase class, to implement the plugin. /// @param[in] sourceFiles Source file (with path). /// @param[in] module Class name of the plugin. /// @param[in] references String array with auxiliary references used by your plugin. /// See notes for defaults used. /// @param[in] objParams Reference to a PropellerCPU of this instance, to be passed /// as a parameter to the constructor of the new plugin class instance. /// @param[in] pluginSystemVersion Witch plugin system version to compile. /// @returns New Plugin class instance compiled (on success), or NULL (on fail). /// @note There are some references already added, so you don't need to include on your /// plugins: /// @li `using System;` @li `using System.Data;` @li `using System.Drawing;` /// @li `using System.Windows.Forms;` @li `using System.Xml;` /// @version v15.03.26 - Modified the logic. static public PluginCommon LoadModule(string[] codeTexts, string[] sourceFiles, string module, string[] references, object objParams, string pluginSystemVersion) { const string chachePath = @".\plugincache\"; string compiledName = AssemblyUtils.CompiledPluginName(module, pluginSystemVersion, ".dll"); CompilerParameters cp = new CompilerParameters( new[] { "System.Windows.Forms.dll", "System.dll", "System.Data.dll", "System.Drawing.dll", "System.Xml.dll" }, chachePath + compiledName, false); if (!Directory.Exists(chachePath)) { Directory.CreateDirectory(chachePath); } cp.TempFiles = new TempFileCollection(chachePath, false); //set directory cp.ReferencedAssemblies.Add(System.Windows.Forms.Application.ExecutablePath); cp.IncludeDebugInformation = false; cp.GenerateExecutable = false; cp.GenerateInMemory = true; cp.CompilerOptions = "/optimize"; cp.WarningLevel = 4; //to do not consider C00618 warning (obsolete PluginBaseV0_0 class) cp.MainClass = "Gear.PluginSupport." + module; //traverse list adding not null nor empty texts foreach (string s in references) //traverse list adding not null or empty texts { if (!string.IsNullOrEmpty(s)) { cp.ReferencedAssemblies.Add(s); } } CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider(); try { //compile source codes into a memory assembly CompilerResults results = provider.CompileAssemblyFromSource(cp, codeTexts); if (results.Errors.HasErrors | results.Errors.HasWarnings) { m_Errors = results.Errors; return(null); } //#if DEBUG // //write the code to file to enable debug // for (int i = 0; i < codeTexts.Length; i++ ) // File.WriteAllText( // codefile.Replace(".cs", string.Format(".{0}.cs",i)), // codeTexts[i] ); //#endif PrintAssembliesLoaded(AppDomain.CurrentDomain, results.CompiledAssembly, true); //then instantiate plugin class object target = results.CompiledAssembly.CreateInstance( module, //string typeName false, //bool ignoreCase BindingFlags.Public | BindingFlags.Instance, //BindingFlags bindingAttr null, //Binder binder (objParams != null) ? //object[] args new object[] { objParams } : null, null, //CultureInfo culture null); //object[] activationAttributes PrintAssembliesLoaded(AppDomain.CurrentDomain, results.CompiledAssembly, false); if (target == null) { CompilerError c = new CompilerError(string.Empty, 0, 0, "CS0103", "The name '" + module + "' does not exist in the current context." + " Does the class name is the same that is declared in c# code?"); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } else if (!PluginSystem.InstanceOneOfValidClasses(target)) { CompilerError c = new CompilerError(string.Empty, 0, 0, "CS0029", "Cannot implicitly convert type '" + target.GetType().FullName + "' to '" + PluginSystem.GetPluginBaseType(pluginSystemVersion).FullName + "'"); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } m_Errors = null; return((PluginCommon)target); } catch (Exception e) { CompilerError c = new CompilerError(string.Empty, 0, 0, "Runtime", string.Format("Plugin '{0}' - {1}", module, e.Message)); m_Errors = new CompilerErrorCollection(new CompilerError[] { c }); return(null); } }