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:
- load the assembly automatically (This is what im asking here)
- This would mean i no longer have to give a path to the dll, and get address the enclosed class's members by hand
- arbitrarily reference it's members
- This mean's I can create an interface to the class without precursor knowledge of its members, kind of like how a foreach loop works on key value pairs.
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.
- What is a
System.Security.Policy.Evidence assemblySecurity
object. - What is the proper parameter for
AppDomain.CreateInstanceAndUnwrap()
- How then do i return this as an object?
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 */
}