I've been following this StackOverflow question. Its the closest thing I can find, but not quite.
Let me explain what my end goal is before my question, I'm making a compiler platform that's web enabled, because of that, I want to do everything in memory (make no files), so I want to be able to compile code, and then be able to reference the objects in the class I just compiled for arbitrary testing. I know how it unsafe it sounds, so input in regard to security is welcome.
My question is how do I compile C# source to memory, and create an instance of that class?
Currently I'm at a step where I can generate valid .dll's and import and use it inside VisualStudio by hand.
My next 2 steps are:
To attempt this entirely in memory I've tried this. (source then explanation)
private object sourceToObj(string source) {
string source = "..."; /*my class*/
CSharpCodeProvider pro = new CSharpCodeProvider();
CompilerParameters params = new CompilerParameters();
params.GenerateInMemory = true;
params.GenerateExecutable = false;
params.ReferencedAssemblies.Add("System.dll");
CompilerResults res = pro.CompileAssemblyFromSource( params, source );
Assembly DOTasm = res.CompiledAssembly;
AppDomain domain = AppDomain.CreateDomain( "thisdomain" );
domain.load( DOTasm , /*???*/ );
domain.CreateInstanceAndUnwrap( DOTasm .FullName, /*???*/ );
return /*???*/;
}
Finally, as this point in the code I'd hope to return some object I can call a property of.
So calling object obj = new sourceToObj(source).class();
or something would be possible.
Going down this path, which may indeed be the wrong path leaves me with 3 unknowns.
System.Security.Policy.Evidence assemblySecurity
object.AppDomain.CreateInstanceAndUnwrap()
Of course this method could be wrong, it's based off the link above which is close, but no turkey.
Edit: After more research I wanted to include an example of a source file.
namespace testNS {
public partial class i18Rule {
private string i18_Name;
private string i18_Value;
public void setName(string s) {
i18_name = s;
}
/* Other obvious functions */
};
};
I believe I made a little bit of progress and went onto the second clause of my question, how to create an instance of it.
I went ahead and used an AppDomain to contain my assembly. I also went the route of writing to disk and reading it int a byte array as done in this question i happened upon Compile c# on the fly.
/* not the final method, see Philips answer for tryLoadCompiledType which validates this works */
private void sourceToUnitTest(string source, callBack CB) {
var pro = new CSharpCodeProvider();
var DOMref = AppDomain.CurrentDomain.GetAssemblies()
.Where(obj => !obj.IsDynamic)
.Select(obj => obj.Location)
.ToArray();
var Cparams = new CompilerParameters( DOMref );
Cparams.OutputAssembly = "SOURCE.DLL";
CompilerResults res = pro.CompileAssemblyFromSource(Cparams, source);
Assembly asm = res.CompiledAssembly;
Type[] allTypes = res.CompiledAssembly.GetTypes();
foreach (Type t in allTypes)
{
TryLoadCompiledType(res, t.ToString());
Debug.WriteLine(t.ToString());
}
/* I don't return I do something with each type here */
}
How do I compile C# source to memory, and create an instance of that class?
I faced a similar issue when I wanted to take source code as input and compile and execute it. This is what I came up with after reading Is it possible to dynamically compile and execute C# code fragments?:
public CompilerResults CompileSource(string sourceCode)
{
var csc = new CSharpCodeProvider(
new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var referencedAssemblies =
AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.FullName.StartsWith("mscorlib", StringComparison.InvariantCultureIgnoreCase))
.Where(a => !a.IsDynamic) //necessary because a dynamic assembly will throw and exception when calling a.Location
.Select(a => a.Location)
.ToArray();
var parameters = new CompilerParameters(
referencedAssemblies);
return csc.CompileAssemblyFromSource(parameters,
sourceCode);
}
Then I have a helper function:
public static object TryLoadCompiledType(this CompilerResults compilerResults, string typeName, params object[] constructorArgs)
{
if (compilerResults.Errors.HasErrors)
{
Log.Warn("Can not TryLoadCompiledType because CompilerResults.HasErrors");
return null;
}
var type = compilerResults.CompiledAssembly.GetType(typeName);
if (null == type)
{
Log.Warn("Compiled Assembly does not contain a type [" + typeName + "]");
return null;
}
return Activator.CreateInstance(type, constructorArgs);
}
So to put it together
public void Example(){
dynamic instance =
CompileSource("namespace Test{public class DynamicCompile{ /*method*/}}")
.TryLoadCompiledType("Test.DynamicCompile");
//can now call methods on 'instance'
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With