EDN Admin
Well-known member
Hello,
In our ASP.NET application we have a feature that allows the user to write C# code as scripts. These scripts are compiled into memory and stored untill they need to be executed. Currently were trying to load assemblies from third parties into these scripts
using a simple mechanic.
If the script contains a region called References, we load all those lines from the region as referenced to assemblies and include them in the compile options when compiling the script. If a User types the following script for example:
<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; #region References
<span style="color:Green; /*
C:ScriptLibrary.dll
*/
<span style="color:Blue; #endregion
<span style="color:Blue; using System;
<span style="color:Blue; using System.Linq;
<span style="color:Blue; using TenForce.Execution.Scripting;
<span style="color:Blue; using TenForce.Execution.Api2;
<span style="color:Blue; using TenForce.Execution.Api2.Objects;
<span style="color:Blue; using ScriptLibrary;
<span style="color:Blue; namespace TenForce.Execution.Scripting
{
<span style="color:Blue; public <span style="color:Blue; class TestScript : Script<ScriptContext>
{
[EntryPoint]
<span style="color:Blue; public <span style="color:Blue; void Run()
{
<span style="color:Blue; var item = (Item)ScriptContext.ScriptParameters.GetParameter(<span style="color:#A31515; "Item");
<span style="color:Blue; var fields = <span style="color:Blue; new System.Collections.Generic.List<ItemField>(item.ItemFields);
<span style="color:Blue; new Welcome().Run(fields.Find(field => field.Type == <span style="color:#A31515; "Title").Value);
}
}
}
[/code]
<br/>
He has a simple script with a reference to C:ScriptLibrary.dll that needs to be loaded in order to run the custom code inside the script. When the user clicks on the save button, we take the source code and compile this in memory to an assembly. The compiling
process is working as we receive no errors or exceptions during the compilation, and the DLL is properly extracted from the source code and added to the compile process.
The problem occurs when we try to execute this script. Im receiving a FileNotFoundException with these details:
{"Could not load file or assembly ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null or one of its dependencies. The system cannot find the file specified.":"ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
The FusionLog included in the Exception contains the following information:
=== Pre-bind state information ===<br/>
LOG: User = NT AUTHORITYNETWORK SERVICE<br/>
LOG: DisplayName = ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null<br/>
(Fully-specified)<br/>
LOG: Appbase = file:///D:/Users/arne.de.herdt.TENFORCE2/Documents/Source/Projects/Robinson/RobinsonWebApp/<br/>
LOG: Initial PrivatePath = D:Usersarne.de.herdt.TENFORCE2DocumentsSourceProjectsRobinsonRobinsonWebAppbin<br/>
Calling assembly : (Unknown).<br/>
===<br/>
LOG: This bind starts in default load context.<br/>
LOG: Using application configuration file: D:Usersarne.de.herdt.TENFORCE2DocumentsSourceProjectsRobinsonRobinsonWebAppweb.config<br/>
LOG: Using host configuration file: C:WindowsMicrosoft.NETFramework64v4.0.30319aspnet.config<br/>
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFramework64v4.0.30319configmachine.config.<br/>
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).<br/>
LOG: The same bind was seen before, and was failed with hr = 0x80070002.
Im not sure what is going wrong now. This was working fine during the POC and I could reference other DLL files and use them inside the scripts.
The reason why we are doing this, is because the original system copied all the referenced DLL files into the bin folder of the web application. But this causes the web application to restart and this is not acceptable. What can we do to ensure the
script can be properly executed regardless of where these DLL files are stored?
We do have a second approach where we include the DLLs in a specific folder. But this is not working either.
The code we use for compiling and referecing this assembly in memory is this:
<div style="color:Black;background-color:White; <pre>
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function will load the required DLL assemblies into the CompilerParameters. The selection is based
<span style="color:Gray; ///<span style="color:Green; upon the API implementation and the dynamic requirements of the source code in the script.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="parameters <span style="color:Green; The CompilerParameters collection holding the assemblies.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The source code of the script.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="assemblyPath <span style="color:Green; The absolute path to the assemblies to be loaded.</param>
<span style="color:Blue; private <span style="color:Blue; void LoadAssemblies(CompilerParameters parameters, <span style="color:Blue; string source, <span style="color:Blue; string assemblyPath)
{
<span style="color:Green; // Now we first include the standard .NET Framework assemblies that are required regardless
<span style="color:Green; // of the API Implementation.
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"mscorlib.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Data.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Xml.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Web.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Core.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Xml.Linq.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Drawing.dll");
<span style="color:Green; // Add the common TenForce DLLs that are required regardless of the API implementation.
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"GemBox.Spreadsheet.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Scripting.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"LanguageResource.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"Framework.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"BUL.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.Objects.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.Implementation.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Api2.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Api2.Implementation.dll");
<span style="color:Green; // Add the dynamic references required by the source and are located in the assembly path.
parameters.ReferencedAssemblies.AddRange(GetDynamicReferences(source));
}
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function will load all the required and dynamic DLL assemblies into the CompilerParameters. The selection is
<span style="color:Gray; ///<span style="color:Green; based upon the API implementation and the dynamic requirements of the source code in the script and the included libraries.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="parameters <span style="color:Green; The <see cref="CompilerParameters"/> collection to store the referenced in.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The original source code of the script beeing loaded.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="assemblyPath <span style="color:Green; The full assembly where all assemblies are stored.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="libraries <span style="color:Green; The additional libraries beeing loaded by the script.</param>
<span style="color:Blue; private <span style="color:Blue; void LoadAssemblies(CompilerParameters parameters, <span style="color:Blue; string source, <span style="color:Blue; string assemblyPath, IEnumerable<<span style="color:Blue; string> libraries)
{
<span style="color:Green; // Load the assemblies for the source code first.
LoadAssemblies(parameters, source, assemblyPath);
<span style="color:Green; // Load the assemblies for all the additional libraries that were including.
<span style="color:Blue; foreach(<span style="color:Blue; string library <span style="color:Blue; in libraries)
parameters.ReferencedAssemblies.AddRange(GetDynamicReferences(library));
}
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function performs the compile operation and return the compiled assembly.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The source code of the script to compile.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="libs <span style="color:Green; A collection of additional libraries to compile the script.</param>
<span style="color:Gray; /// <span style="color:Gray; <returns><span style="color:Green; The compiled assembly.</returns>
<span style="color:Blue; internal Assembly Compile(<span style="color:Blue; string source, List<<span style="color:Blue; string> libs)
{
<span style="color:Blue; var libraries = <span style="color:Blue; new List<<span style="color:Blue; string>(libs);
CodeDomProvider codeProvider = <span style="color:Blue; new CSharpCodeProvider(<span style="color:Blue; new Dictionary<<span style="color:Blue; string, <span style="color:Blue; string> { { <span style="color:#A31515; "CompilerVersion", <span style="color:#A31515; "v4.0" } });
<span style="color:Blue; var compilerParams = <span style="color:Blue; new CompilerParameters
{
CompilerOptions = <span style="color:#A31515; "/target:library /optimize /lib:C:\Windows\assembly",
GenerateExecutable = <span style="color:Blue; false,
GenerateInMemory = <span style="color:Blue; true,
IncludeDebugInformation = <span style="color:Blue; true,
TreatWarningsAsErrors = <span style="color:Blue; false
};
<span style="color:Blue; string assemblyDllPath = Path.GetDirectoryName(<span style="color:Blue; new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
<span style="color:Green; // Load all the required assemblies depending on the api implementation.
LoadAssemblies(compilerParams, source, assemblyDllPath, libraries);
<span style="color:Green; // If the library path has been specified, then we need to include this to the CompilerOptions, so that
<span style="color:Green; // relative file paths can be found in the library we specified.
<span style="color:Blue; if(!<span style="color:Blue; string.IsNullOrEmpty(_libraryPath))
compilerParams.CompilerOptions += <span style="color:Blue; string.Format(<span style="color:#A31515; " /lib:"{0}"", _libraryPath);
<span style="color:Blue; var path = Path.Combine(Path.GetTempPath(), <span style="color:#A31515; "TF-" + Guid.NewGuid().ToString().ToUpper());
<span style="color:Green; // replace resx-files from provided libraries with compatible dlls
<span style="color:Blue; var resxs = libraries.FindAll(lb => lb.EndsWith(<span style="color:#A31515; ".resx", StringComparison.OrdinalIgnoreCase));
<span style="color:Blue; var tmpFiles = <span style="color:Blue; new List<<span style="color:Blue; string>();
<span style="color:Blue; if (resxs.Count > 0)
{
<span style="color:Blue; if (!Directory.Exists(path)) Directory.CreateDirectory(path);
<span style="color:Blue; foreach (<span style="color:Blue; var resx <span style="color:Blue; in resxs)
{
<span style="color:Green; // Get the resources filename
<span style="color:Blue; var resourceFilename = Path.GetFileNameWithoutExtension(resx);
<span style="color:Blue; var filename = Path.Combine(path, resourceFilename + <span style="color:#A31515; ".resources");
File.Delete(filename);
tmpFiles.Add(filename);
<span style="color:Green; // Create a ResXResourceReader for the file items.resx.
Stream stream = File.Open(resx, FileMode.Open, FileAccess.Read, FileShare.Read);
<span style="color:Blue; var rsxr = <span style="color:Blue; new ResXResourceReader(stream);
<span style="color:Green; // Create a ResXResourceReader for the file items.resources.
IResourceWriter writer = <span style="color:Blue; new ResourceWriter(filename);
<span style="color:Green; // Iterate through the resources and add resources to the resource writer.
IDictionary dictionary = <span style="color:Blue; new Dictionary<<span style="color:Blue; string, <span style="color:Blue; string>();
<span style="color:Blue; foreach (DictionaryEntry d <span style="color:Blue; in rsxr)
{
<span style="color:Blue; var k = d.Key.ToString();
<span style="color:Blue; var v = d.Value.ToString();
dictionary.Add(k, v);
writer.AddResource(k, v);
}
<span style="color:Green; // Close the reader.
rsxr.Close();
stream.Close();
writer.Close();
compilerParams.EmbeddedResources.Add(filename);
<span style="color:Blue; string[] errors;
<span style="color:Blue; var provider = <span style="color:Blue; new CSharpCodeProvider(); <span style="color:Green; // c#-code compiler
<span style="color:Blue; var cu = StronglyTypedResourceBuilder.Create(dictionary, resourceFilename ?? <span style="color:Blue; string.Empty, <span style="color:#A31515; "", provider, <span style="color:Blue; false, <span style="color:Blue; out errors);
<span style="color:Blue; var options = <span style="color:Blue; new CodeGeneratorOptions
{
BracingStyle = <span style="color:#A31515; "C",
BlankLinesBetweenMembers = <span style="color:Blue; false,
IndentString = <span style="color:#A31515; "t"
};
<span style="color:Blue; var tw = <span style="color:Blue; new StringWriter();
provider.GenerateCodeFromCompileUnit(cu, tw, options);
<span style="color:Blue; var libCode = tw.ToString();
tw.Close();
<span style="color:Blue; if (!libraries.Contains(libCode))
libraries.Add(libCode);
}
libraries.RemoveAll(lb => lb.EndsWith(<span style="color:#A31515; ".resx", StringComparison.OrdinalIgnoreCase));
}
<span style="color:Green; // actually compile the code
CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, <span style="color:Blue; new List<<span style="color:Blue; string>(libraries) { source }.ToArray());
<span style="color:Green; // remove the temporary files
<span style="color:Blue; foreach (<span style="color:Blue; var file <span style="color:Blue; in tmpFiles)
File.Delete(file);
<span style="color:Green; // remove the resource directory
<span style="color:Blue; if(Directory.Exists(path)) Directory.Delete(path);
<span style="color:Blue; if (results.Errors.HasErrors)
{
<span style="color:Blue; var sb = <span style="color:Blue; new StringBuilder(<span style="color:#A31515; "Compilation error :nt");
<span style="color:Blue; foreach (CompilerError error <span style="color:Blue; in results.Errors)
sb.AppendLine(<span style="color:#A31515; "t" + error.ErrorText);
<span style="color:Blue; throw <span style="color:Blue; new Exception(sb.ToString());
}
<span style="color:Green; //get a hold of the actual assembly that was generated
Assembly generatedAssembly = results.CompiledAssembly;
<span style="color:Green; // move to some app startup place (this only needs to be set once)
<span style="color:Blue; if (!API.Factory.IsAPIImplementationTypeSet)
{ API.Factory.SetAPIImplementation(Assembly.LoadFile(assemblyDllPath + <span style="color:#A31515; "\TenForce.Execution.API.Implementation.dll").GetType(<span style="color:#A31515; "TenForce.Execution.API.Implementation.API")); }
<span style="color:Green; // Set the implementation type for the API2 as well. This should only be set once.
<span style="color:Blue; if (!Api2.Factory.ImplementationSet)
{ Api2.Factory.SetImplementation(Assembly.LoadFile(assemblyDllPath + <span style="color:#A31515; "\TenForce.Execution.Api2.Implementation.dll").GetType(<span style="color:#A31515; "TenForce.Execution.Api2.Implementation.Api")); }
<span style="color:Blue; return generatedAssembly;
}
[/code]
<br/>
<br/>
View the full article
In our ASP.NET application we have a feature that allows the user to write C# code as scripts. These scripts are compiled into memory and stored untill they need to be executed. Currently were trying to load assemblies from third parties into these scripts
using a simple mechanic.
If the script contains a region called References, we load all those lines from the region as referenced to assemblies and include them in the compile options when compiling the script. If a User types the following script for example:
<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; #region References
<span style="color:Green; /*
C:ScriptLibrary.dll
*/
<span style="color:Blue; #endregion
<span style="color:Blue; using System;
<span style="color:Blue; using System.Linq;
<span style="color:Blue; using TenForce.Execution.Scripting;
<span style="color:Blue; using TenForce.Execution.Api2;
<span style="color:Blue; using TenForce.Execution.Api2.Objects;
<span style="color:Blue; using ScriptLibrary;
<span style="color:Blue; namespace TenForce.Execution.Scripting
{
<span style="color:Blue; public <span style="color:Blue; class TestScript : Script<ScriptContext>
{
[EntryPoint]
<span style="color:Blue; public <span style="color:Blue; void Run()
{
<span style="color:Blue; var item = (Item)ScriptContext.ScriptParameters.GetParameter(<span style="color:#A31515; "Item");
<span style="color:Blue; var fields = <span style="color:Blue; new System.Collections.Generic.List<ItemField>(item.ItemFields);
<span style="color:Blue; new Welcome().Run(fields.Find(field => field.Type == <span style="color:#A31515; "Title").Value);
}
}
}
[/code]
<br/>
He has a simple script with a reference to C:ScriptLibrary.dll that needs to be loaded in order to run the custom code inside the script. When the user clicks on the save button, we take the source code and compile this in memory to an assembly. The compiling
process is working as we receive no errors or exceptions during the compilation, and the DLL is properly extracted from the source code and added to the compile process.
The problem occurs when we try to execute this script. Im receiving a FileNotFoundException with these details:
{"Could not load file or assembly ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null or one of its dependencies. The system cannot find the file specified.":"ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"}
The FusionLog included in the Exception contains the following information:
=== Pre-bind state information ===<br/>
LOG: User = NT AUTHORITYNETWORK SERVICE<br/>
LOG: DisplayName = ScriptLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null<br/>
(Fully-specified)<br/>
LOG: Appbase = file:///D:/Users/arne.de.herdt.TENFORCE2/Documents/Source/Projects/Robinson/RobinsonWebApp/<br/>
LOG: Initial PrivatePath = D:Usersarne.de.herdt.TENFORCE2DocumentsSourceProjectsRobinsonRobinsonWebAppbin<br/>
Calling assembly : (Unknown).<br/>
===<br/>
LOG: This bind starts in default load context.<br/>
LOG: Using application configuration file: D:Usersarne.de.herdt.TENFORCE2DocumentsSourceProjectsRobinsonRobinsonWebAppweb.config<br/>
LOG: Using host configuration file: C:WindowsMicrosoft.NETFramework64v4.0.30319aspnet.config<br/>
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFramework64v4.0.30319configmachine.config.<br/>
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).<br/>
LOG: The same bind was seen before, and was failed with hr = 0x80070002.
Im not sure what is going wrong now. This was working fine during the POC and I could reference other DLL files and use them inside the scripts.
The reason why we are doing this, is because the original system copied all the referenced DLL files into the bin folder of the web application. But this causes the web application to restart and this is not acceptable. What can we do to ensure the
script can be properly executed regardless of where these DLL files are stored?
We do have a second approach where we include the DLLs in a specific folder. But this is not working either.
The code we use for compiling and referecing this assembly in memory is this:
<div style="color:Black;background-color:White; <pre>
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function will load the required DLL assemblies into the CompilerParameters. The selection is based
<span style="color:Gray; ///<span style="color:Green; upon the API implementation and the dynamic requirements of the source code in the script.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="parameters <span style="color:Green; The CompilerParameters collection holding the assemblies.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The source code of the script.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="assemblyPath <span style="color:Green; The absolute path to the assemblies to be loaded.</param>
<span style="color:Blue; private <span style="color:Blue; void LoadAssemblies(CompilerParameters parameters, <span style="color:Blue; string source, <span style="color:Blue; string assemblyPath)
{
<span style="color:Green; // Now we first include the standard .NET Framework assemblies that are required regardless
<span style="color:Green; // of the API Implementation.
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"mscorlib.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Data.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Xml.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Web.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Core.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Xml.Linq.dll");
parameters.ReferencedAssemblies.Add(<span style="color:#A31515; @"System.Drawing.dll");
<span style="color:Green; // Add the common TenForce DLLs that are required regardless of the API implementation.
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"GemBox.Spreadsheet.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Scripting.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"LanguageResource.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"Framework.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"BUL.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.Objects.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.API.Implementation.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Api2.dll");
parameters.ReferencedAssemblies.Add(assemblyPath + <span style="color:#A31515; @"TenForce.Execution.Api2.Implementation.dll");
<span style="color:Green; // Add the dynamic references required by the source and are located in the assembly path.
parameters.ReferencedAssemblies.AddRange(GetDynamicReferences(source));
}
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function will load all the required and dynamic DLL assemblies into the CompilerParameters. The selection is
<span style="color:Gray; ///<span style="color:Green; based upon the API implementation and the dynamic requirements of the source code in the script and the included libraries.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="parameters <span style="color:Green; The <see cref="CompilerParameters"/> collection to store the referenced in.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The original source code of the script beeing loaded.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="assemblyPath <span style="color:Green; The full assembly where all assemblies are stored.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="libraries <span style="color:Green; The additional libraries beeing loaded by the script.</param>
<span style="color:Blue; private <span style="color:Blue; void LoadAssemblies(CompilerParameters parameters, <span style="color:Blue; string source, <span style="color:Blue; string assemblyPath, IEnumerable<<span style="color:Blue; string> libraries)
{
<span style="color:Green; // Load the assemblies for the source code first.
LoadAssemblies(parameters, source, assemblyPath);
<span style="color:Green; // Load the assemblies for all the additional libraries that were including.
<span style="color:Blue; foreach(<span style="color:Blue; string library <span style="color:Blue; in libraries)
parameters.ReferencedAssemblies.AddRange(GetDynamicReferences(library));
}
<span style="color:Gray; /// <span style="color:Gray; <summary>
<span style="color:Gray; /// <span style="color:Gray; <para><span style="color:Green; This function performs the compile operation and return the compiled assembly.</para>
<span style="color:Gray; /// <span style="color:Gray; </summary>
<span style="color:Gray; /// <span style="color:Gray; <param name="source <span style="color:Green; The source code of the script to compile.</param>
<span style="color:Gray; /// <span style="color:Gray; <param name="libs <span style="color:Green; A collection of additional libraries to compile the script.</param>
<span style="color:Gray; /// <span style="color:Gray; <returns><span style="color:Green; The compiled assembly.</returns>
<span style="color:Blue; internal Assembly Compile(<span style="color:Blue; string source, List<<span style="color:Blue; string> libs)
{
<span style="color:Blue; var libraries = <span style="color:Blue; new List<<span style="color:Blue; string>(libs);
CodeDomProvider codeProvider = <span style="color:Blue; new CSharpCodeProvider(<span style="color:Blue; new Dictionary<<span style="color:Blue; string, <span style="color:Blue; string> { { <span style="color:#A31515; "CompilerVersion", <span style="color:#A31515; "v4.0" } });
<span style="color:Blue; var compilerParams = <span style="color:Blue; new CompilerParameters
{
CompilerOptions = <span style="color:#A31515; "/target:library /optimize /lib:C:\Windows\assembly",
GenerateExecutable = <span style="color:Blue; false,
GenerateInMemory = <span style="color:Blue; true,
IncludeDebugInformation = <span style="color:Blue; true,
TreatWarningsAsErrors = <span style="color:Blue; false
};
<span style="color:Blue; string assemblyDllPath = Path.GetDirectoryName(<span style="color:Blue; new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
<span style="color:Green; // Load all the required assemblies depending on the api implementation.
LoadAssemblies(compilerParams, source, assemblyDllPath, libraries);
<span style="color:Green; // If the library path has been specified, then we need to include this to the CompilerOptions, so that
<span style="color:Green; // relative file paths can be found in the library we specified.
<span style="color:Blue; if(!<span style="color:Blue; string.IsNullOrEmpty(_libraryPath))
compilerParams.CompilerOptions += <span style="color:Blue; string.Format(<span style="color:#A31515; " /lib:"{0}"", _libraryPath);
<span style="color:Blue; var path = Path.Combine(Path.GetTempPath(), <span style="color:#A31515; "TF-" + Guid.NewGuid().ToString().ToUpper());
<span style="color:Green; // replace resx-files from provided libraries with compatible dlls
<span style="color:Blue; var resxs = libraries.FindAll(lb => lb.EndsWith(<span style="color:#A31515; ".resx", StringComparison.OrdinalIgnoreCase));
<span style="color:Blue; var tmpFiles = <span style="color:Blue; new List<<span style="color:Blue; string>();
<span style="color:Blue; if (resxs.Count > 0)
{
<span style="color:Blue; if (!Directory.Exists(path)) Directory.CreateDirectory(path);
<span style="color:Blue; foreach (<span style="color:Blue; var resx <span style="color:Blue; in resxs)
{
<span style="color:Green; // Get the resources filename
<span style="color:Blue; var resourceFilename = Path.GetFileNameWithoutExtension(resx);
<span style="color:Blue; var filename = Path.Combine(path, resourceFilename + <span style="color:#A31515; ".resources");
File.Delete(filename);
tmpFiles.Add(filename);
<span style="color:Green; // Create a ResXResourceReader for the file items.resx.
Stream stream = File.Open(resx, FileMode.Open, FileAccess.Read, FileShare.Read);
<span style="color:Blue; var rsxr = <span style="color:Blue; new ResXResourceReader(stream);
<span style="color:Green; // Create a ResXResourceReader for the file items.resources.
IResourceWriter writer = <span style="color:Blue; new ResourceWriter(filename);
<span style="color:Green; // Iterate through the resources and add resources to the resource writer.
IDictionary dictionary = <span style="color:Blue; new Dictionary<<span style="color:Blue; string, <span style="color:Blue; string>();
<span style="color:Blue; foreach (DictionaryEntry d <span style="color:Blue; in rsxr)
{
<span style="color:Blue; var k = d.Key.ToString();
<span style="color:Blue; var v = d.Value.ToString();
dictionary.Add(k, v);
writer.AddResource(k, v);
}
<span style="color:Green; // Close the reader.
rsxr.Close();
stream.Close();
writer.Close();
compilerParams.EmbeddedResources.Add(filename);
<span style="color:Blue; string[] errors;
<span style="color:Blue; var provider = <span style="color:Blue; new CSharpCodeProvider(); <span style="color:Green; // c#-code compiler
<span style="color:Blue; var cu = StronglyTypedResourceBuilder.Create(dictionary, resourceFilename ?? <span style="color:Blue; string.Empty, <span style="color:#A31515; "", provider, <span style="color:Blue; false, <span style="color:Blue; out errors);
<span style="color:Blue; var options = <span style="color:Blue; new CodeGeneratorOptions
{
BracingStyle = <span style="color:#A31515; "C",
BlankLinesBetweenMembers = <span style="color:Blue; false,
IndentString = <span style="color:#A31515; "t"
};
<span style="color:Blue; var tw = <span style="color:Blue; new StringWriter();
provider.GenerateCodeFromCompileUnit(cu, tw, options);
<span style="color:Blue; var libCode = tw.ToString();
tw.Close();
<span style="color:Blue; if (!libraries.Contains(libCode))
libraries.Add(libCode);
}
libraries.RemoveAll(lb => lb.EndsWith(<span style="color:#A31515; ".resx", StringComparison.OrdinalIgnoreCase));
}
<span style="color:Green; // actually compile the code
CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, <span style="color:Blue; new List<<span style="color:Blue; string>(libraries) { source }.ToArray());
<span style="color:Green; // remove the temporary files
<span style="color:Blue; foreach (<span style="color:Blue; var file <span style="color:Blue; in tmpFiles)
File.Delete(file);
<span style="color:Green; // remove the resource directory
<span style="color:Blue; if(Directory.Exists(path)) Directory.Delete(path);
<span style="color:Blue; if (results.Errors.HasErrors)
{
<span style="color:Blue; var sb = <span style="color:Blue; new StringBuilder(<span style="color:#A31515; "Compilation error :nt");
<span style="color:Blue; foreach (CompilerError error <span style="color:Blue; in results.Errors)
sb.AppendLine(<span style="color:#A31515; "t" + error.ErrorText);
<span style="color:Blue; throw <span style="color:Blue; new Exception(sb.ToString());
}
<span style="color:Green; //get a hold of the actual assembly that was generated
Assembly generatedAssembly = results.CompiledAssembly;
<span style="color:Green; // move to some app startup place (this only needs to be set once)
<span style="color:Blue; if (!API.Factory.IsAPIImplementationTypeSet)
{ API.Factory.SetAPIImplementation(Assembly.LoadFile(assemblyDllPath + <span style="color:#A31515; "\TenForce.Execution.API.Implementation.dll").GetType(<span style="color:#A31515; "TenForce.Execution.API.Implementation.API")); }
<span style="color:Green; // Set the implementation type for the API2 as well. This should only be set once.
<span style="color:Blue; if (!Api2.Factory.ImplementationSet)
{ Api2.Factory.SetImplementation(Assembly.LoadFile(assemblyDllPath + <span style="color:#A31515; "\TenForce.Execution.Api2.Implementation.dll").GetType(<span style="color:#A31515; "TenForce.Execution.Api2.Implementation.Api")); }
<span style="color:Blue; return generatedAssembly;
}
[/code]
<br/>
<br/>
View the full article