Add new cs files on the fly to load my application launcher?
I have a command handler that basically works like this:
ControlList.Handlers[CommandType.MyCommandComesHere].Handle(data);
Handlers
is Dictionary<CommandType, ICommandHandler>
, and CommandType
is an enumeration.
Handle
its rotation will lead to the following:
using System;
using log4net;
namespace My_Application
{
public class MyCommand : ICommandHandler
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyCommand));
public void Handle(Events data)
{
Console.WriteLine("I can load cs files on the fly yay!!");
}
}
}
My question is, how can I make my application compile and allow me to use this cs file while it is running?
Any simple example of this would be greatly appreciated but not required as long as I can get some guidance as to what I need to look for as I am not even sure if I need this to happen.
To put it simply, I am currently trying to figure out how to load the cs file into my application which is already compiled and currently running.
source to share
Using CodeDOM, you must first create a compiler provider . (You can set GenerateExecutable
on false
and GenerateInMemory
on true
for your own purposes.)
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
Then you can compile the assembly with CompileAssemblyFromSource
and return CompilerResults
from it. From this returned object, get a reference to the generated assembly using its property CompiledAssembly
.
var results = csc.CompileAssemblyFromSource(parameters, "contents of the .cs file");
var assembly = results.CompiledAssembly;
Then you can use reflection to instantiate from that assembly and call methods on them.
var instance = assembly.CreateInstance("MyCommand");
// etc...
Alternatively, if you are only interested in short snippets of code, you should probably use Roslyn . First you need to create ScriptEngine
.
var engine = new ScriptEngine();
Then you can just Execute
string on it - or Execute<T>
if you're sure the expression in the string returns the type being assigned T
.
var myObject = engine.Execute("1+1");
var myInt = engine.Execute<int>("1+1");
It's definitely more urgent, so it's worth checking out if it will serve your purpose.
source to share
I was looking for different ways to achieve this goal and found the cs script library easy and handy. Here is the code snippet how I use it. It runs the cs code in the application domain, so it assumes the compiled cs script comes from a trusted source.
using CSScriptLibrary;
using csscript;
using System.CodeDom.Compiler;
using System.Reflection;
//Method example - variable script contains cs code
//This is used to compile cs to DLL and save DLL to a defined location
public Assembly GetAssembly(string script, string assemblyFileName)
{
Assembly assembly;
CSScript.CacheEnabled = true;
try
{
bool debugBuild = false;
#if DEBUG
debugBuild = true;
#endif
if (assemblyFileName == null)
assembly = CSScript.LoadCode(script, null);
else
assembly = CSScript.LoadCode(script, assemblyFileName, debugBuild, null);
return assembly;
}
catch (CompilerException e)
{
//Handle compiler exceptions
}
}
/// <summary>
/// Runs the code either form script text or precompiled DLL
/// </summary>
public void Run(string script)
{
try
{
string tmpPath = GetPathToDLLs(); //Path, where you store precompiled DLLs
string assemblyFileName;
Assembly assembly = null;
if (Directory.Exists(tmpPath))
{
assemblyFileName = Path.Combine(tmpPath, GetExamScriptFileName(exam));
if (File.Exists(assemblyFileName))
{
try
{
assembly = Assembly.LoadFrom(assemblyFileName); //Načtení bez kompilace
}
catch (Exception exAssemblyLoad)
{
Tools.LogError(exAssemblyLoad.Message);
assembly = null;
}
}
}
else
assemblyFileName = null;
//If assembly not found, compile it form script string
if (assembly ==null)
assembly = GetAssembly(script, assemblyFileName);
AsmHelper asmHelper = new AsmHelper(assembly);
//This is how I use the compiled assembly - it depends on your actual code
ICalculateScript calcScript = (ICalculateScript)asmHelper.CreateObject(GetExamScriptClassName(exam));
cex = calcScript.Calculate(this, exam);
Debug.Print("***** Calculated {0} ****", exam.ZV.ZkouskaVzorkuID);
}
catch (Exception e)
{
//handle exceptions
}
}
source to share