A) compiling C # EXE and DLL on the fly is relatively simple.
B) Executing an EXE means that a new application is running. DLL loading means that methods and functions can be used in cases that can be shared between applications or projects.
Now the fastest and easiest way to compile the EXE (or with minor modifications, the DLL) can be found in MSDN or for your convenience:
private bool CompileCSharpCode(string script) { lvErrors.Items.Clear(); try { CSharpCodeProvider provider = new CSharpCodeProvider(); // Build the parameters for source compilation. CompilerParameters cp = new CompilerParameters { GenerateInMemory = false, GenerateExecutable = false, // True = EXE, False = DLL IncludeDebugInformation = true, OutputAssembly = "eventHandler.dll", // Compilation name }; // Add in our included libs. cp.ReferencedAssemblies.Add("System.dll"); cp.ReferencedAssemblies.Add("System.Windows.Forms.dll"); cp.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll"); // Invoke compilation. This works from a string, but you can also load from a file using FromFile() CompilerResults cr = provider.CompileAssemblyFromSource(cp, script); if (cr.Errors.Count > 0) { // Display compilation errors. foreach (CompilerError ce in cr.Errors) { //I have a listview to display errors. lvErrors.Items.Add(ce.ToString()); } return false; } else { lvErrors.Items.Add("Compiled Successfully."); } provider.Dispose(); } catch (Exception e) { // never really reached, but better safe than sorry? lvErrors.Items.Add("SEVERE! "+e.Message + e.StackTrace.ToString()); return false; } return true; }
Now that you can compile on the fly, there are several differences between loading the DLL. Typically, you add it as a link in Visual Studios to compile into a project. This is pretty easy, and you probably did it many times, but we want to use it in our current project, and we cannot really require the user to recompile the whole project every time they want to test their new DLL, so I’ll just Discuss how to download the library on the fly. Another term here would be "software." To do this, after successful compilation, we load the assembly as follows:
Assembly assembly = Assembly.LoadFrom("yourfilenamehere.dll");
If you have an AppDomain, you can try the following:
Assembly assembly = domain.Load(AssemblyName.GetAssemblyName("yourfilenamehere.dll"));
Now that lib is “referenced”, we can open and use it. There are two ways to do this. One requires you to know if the method has parameters, the other will check you. I will do it later, you can check the MSDN for another.
// replace with your namespace.class Type type = assembly.GetType("company.project"); if (type != null) { // replace with your function name MethodInfo method = type.GetMethod("method"); if (method != null) { object result = null; ParameterInfo[] parameters = method.GetParameters(); object classInstance = Activator.CreateInstance(type, null); if (parameters.Length == 0) // takes no parameters { // method A: result = method.Invoke(classInstance, null); // method B: //result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, null); } else // takes 1+ parameters { object[] parametersArray = new object[] { }; // add parameters here // method A: result = method.Invoke(classInstance, parametersArray); // method B: //result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, parametersArray); } } }
PROBLEM: The first compiler works fine. The first performance works fine. However, an attempt to recompile will be a mistake stating that your * .PDP (debugger base) is being used. I heard some hints of marshaling and AppDomains, but I did not quite understand the problem. Re-compilation will fail only after loading the DLL.
Current attempt by Marshaling && AppDomain:
class ProxyDomain : MarshalByRefObject { private object _instance; public object Instance { get { return _instance; } } private AppDomain _domain; public AppDomain Domain { get { return _domain; } } public void CreateDomain(string friendlyName, System.Security.Policy.Evidence securityinfo) { _domain = AppDomain.CreateDomain(friendlyName, securityinfo); } public void UnloadDomain() { try { AppDomain.Unload(_domain); } catch (ArgumentNullException dne) {
Errors on _instance = _domain.CreateInstanceAndUnwrap (_assembly.FullName, typeName); saying that my Assembly is not serializable. I tried adding the [Serializable] tag to my class with no luck. Still exploring corrections.
It seems that things can get a little confused when you cannot see how they are used, so making it easier?
private void pictureBox1_Click(object sender, EventArgs e) { pd.UnloadDomain(); if (CompileCSharpCode(header + tScript.Text + footer)) { try { pd.CreateDomain("DLLDomain", null); pd.LoadAssembly("eventHandler.dll", "Events.eventHandler"); pd.CreateInstanceAndUnwrap("Events.eventHandler"); // Assembly not Serializable error! /*if (pd.type != null) { MethodInfo onConnect = pd.type.GetMethod("onConnect"); if (onConnect != null) { object result = null; ParameterInfo[] parameters = onConnect.GetParameters(); object classInstance = Activator.CreateInstance(pd.type, null); if (parameters.Length == 0) { result = pd.type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, null); //result = onConnect.Invoke(classInstance, null); } else { object[] parametersArray = new object[] { }; //result = onConnect.Invoke(classInstance, parametersArray); //result = type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, parametersArray); } } }*/ //assembly = Assembly.LoadFrom(null); } catch (Exception er) { MessageBox.Show("There was an error executing the script.\n>" + er.Message + "\n - " + er.StackTrace.ToString()); } finally { } } }