} // END public CodeFile() /// <summary> /// Sets the code into a variable, and compiles it, /// returning (as out variables) information about the /// instance it represents. /// </summary> /// <param name="CodeLines"></param> /// <returns></returns> public List <String> SetAndCompileCSCode(String[] CodeLines, out String pFullTypeName, out String pModuleName, out List <String> pConstructors, out List <String> pMembers, out List <String> pFields, out List <String> pMethods, out List <String> pProperties) { List <String> RetVal = new List <String>(); CodeDOMProcessor oDOM = null; List <String> DOMRetVal = null; pFullTypeName = ""; pModuleName = ""; pConstructors = new List <String>(); pMembers = new List <String>(); pFields = new List <String>(); pMethods = new List <String>(); pProperties = new List <String>(); try { m_CodeLines = CodeLines; m_RefAssemblies = new List <String>(); m_MainClassName = ""; Boolean OK2UseLine = true; m_Code2Use = ""; // Go through code finding the "using" statements and specific execution-time information foreach (String CodeLine in CodeLines) { OK2UseLine = true; if (CodeLine.StartsWith("using ", StringComparison.CurrentCultureIgnoreCase)) { // We pull out the using lines to define assemblies, and exclude // from the code to compile. "using" is not for the compiler, but for the IDE. OK2UseLine = false; String UsingName = Strings.Replace(CodeLine, "using", "", 1, 1, CompareMethod.Text).Trim(); if (UsingName.EndsWith(";")) { UsingName = UsingName.Substring(0, UsingName.Length - 1); } m_RefAssemblies.Add(UsingName); } // END if (CodeLine.StartsWith("using ", StringComparison.CurrentCultureIgnoreCase)) // Here is where we get the first class name to use as the main class name. // This code could be refined to find all class names and let the user define the main class name. if ((CodeLine.IndexOf(" class ", 0, StringComparison.CurrentCultureIgnoreCase) > 0) && (m_MainClassName.Length == 0)) { OK2UseLine = true; Int32 ClassNameStart = CodeLine.IndexOf(" class ", 0, StringComparison.CurrentCultureIgnoreCase) + 7; m_MainClassName = CodeLine.Substring(ClassNameStart); } // END if (CodeLine.IndexOf(" class ", 0, StringComparison.CurrentCultureIgnoreCase) > 0) // If the line is a compilable line, it is added to the list. if (OK2UseLine) { m_Code2Use += CodeLine + Environment.NewLine; } } // END foreach (String CodeLine in CodeLines) // Instantiate the object to process the compilable code. oDOM = new CodeDOMProcessor(); // Compile the code, and get back info on it. // DOMRetVal is a list of messages on why it did or did not compile. DOMRetVal = oDOM.Compile(Code2Use, RefAssemblies, m_MainClassName, out pFullTypeName, out pModuleName, out pConstructors, out pMembers, out pFields, out pMethods, out pProperties); // Not having any referenced assemblies is not good. if (RefAssemblies.Count == 0) { RetVal.Add("No references specified." + Environment.NewLine); } else { // Tell the user what assemblies we found. for (int i = 0; i < RefAssemblies.Count; i++) { RetVal.Add($"Reference [{(i + 1).ToString()}] is [{RefAssemblies[i]}].{Environment.NewLine}"); } // END for (int i = 0; i < RefAssemblies.Count; i++) } if (DOMRetVal != null) { if (DOMRetVal.Count > 0) { foreach (String RetMsg in DOMRetVal) { RetVal.Add(RetMsg + Environment.NewLine); } // END foreach (String RetMsg in DOMRetVal) m_CompiledOK = false; } // END if (DOMRetVal.Count > 0) else { RetVal.Add("No compile messages." + Environment.NewLine); m_CompiledOK = true; } } // END if (RetVal != null) else { RetVal.Add("No compile messages." + Environment.NewLine); m_CompiledOK = true; } } // END try catch (Exception exUnhandled) { // Insert your exception handling code here. // This is only temporary. System.Windows.Forms.MessageBox.Show($"Error Message [{exUnhandled.Message}]{Environment.NewLine}Error Source [{exUnhandled.Source}]", "Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); } // END Catch finally { if (oDOM != null) { oDOM.Dispose(); oDOM = null; } if (DOMRetVal != null) { DOMRetVal.Clear(); DOMRetVal = null; } } // END finally return(RetVal); } // END public List<String> SetAndCompileCSCode(String[] CodeLines)
} // END private void CompileButton_Click(object sender, EventArgs e) /// <summary> /// This method compiles the file read in (or typed in), and optionally executes it. /// </summary> /// <param name="pExecute"></param> private void DoCompile(Boolean pExecute) { // Get the code lines to execute. // The user is allowed to edit the code. String[] CodeLines = CSTextBox.Lines; // This class is used to check the code, and contain the code to execute. CodeFile DOMChecker = new CodeFile(); String pFullTypeName = ""; String pModuleName = ""; List <String> pConstructors = new List <String>(); List <String> pMembers = new List <String>(); List <String> pFields = new List <String>(); List <String> pMethods = new List <String>(); List <String> pProperties = new List <String>(); // Clear the results textbox. ResultsTextBox.Text = ""; // Compile the code and get some results for the UI. // The code can be altered to get more useful objects for methods, properties, parameters, etc. // for automating the use of the external code. List <String> CheckerResults = DOMChecker.SetAndCompileCSCode(CodeLines, out pFullTypeName, out pModuleName, out pConstructors, out pMembers, out pFields, out pMethods, out pProperties); // Build the display text. StringBuilder SB = new StringBuilder(); if (CheckerResults.Count > 0) { SB.AppendLine("Results of Compile:"); if (!DOMChecker.CompiledOK) { SB.AppendLine("Compile failed. The C# compiler does not count comment lines"); SB.AppendLine("or 'using' statement lines when giving line numbers."); SB.AppendLine(""); } // END if (!DOMChecker.CompiledOK) foreach (String Result in CheckerResults) { SB.AppendLine(" " + Result); } } // END if (CheckerResults.Count > 0) SB.AppendLine(" "); if (DOMChecker.CompiledOK) { // If it compiled OK, then show some info about what we found. SB.AppendLine("Full Type Name: " + pFullTypeName); SB.AppendLine("In-memory Module Name: " + pModuleName); SB.AppendLine("Constructor(s):"); foreach (String sConstructor in pConstructors) { SB.AppendLine(" " + sConstructor); } SB.AppendLine(" "); SB.AppendLine("Member(s):"); foreach (String sMember in pMembers) { SB.AppendLine(" " + sMember); } SB.AppendLine(" "); SB.AppendLine("Fields(s):"); foreach (String sField in pFields) { SB.AppendLine(" " + sField); } SB.AppendLine(" "); SB.AppendLine("Method(s):"); foreach (String sMethod in pMethods) { SB.AppendLine(" " + sMethod); } SB.AppendLine(" "); SB.AppendLine("Properties(s):"); foreach (String sProperty in pProperties) { SB.AppendLine(" " + sProperty); } SB.AppendLine(" "); ResultsTextBox.Text = SB.ToString(); SB.Clear(); SB = null; String Msg = ""; if (pExecute) { // if we passed in a flag to execute the code, then // if it compiled, we can execute it. For this example, the // method name and parameters are hardcoded. // In a production setting, the object you get back from compiling can be used to // to allow a user or code to dynamically pick methods and properties to execute // with appropriate parameters. if (DOMChecker.CompiledOK) { CodeDOMProcessor oDOM = new CodeDOMProcessor(); Object[] MethodParams = new Object[] { "Hello World!", "Script Results", new TestObject() }; // Method and params are hard-coded for the Test.cs file. List <String> RetVal = oDOM.CompileAndExecute(DOMChecker.Code2Use, DOMChecker.RefAssemblies, DOMChecker.MainClassName, "ShowMessage", MethodParams); if (RetVal != null) { if (RetVal.Count > 0) { Msg = ""; foreach (String RetMsg in RetVal) { Msg += RetMsg + Environment.NewLine; } // END foreach (String RetMsg in RetVal) if (Msg.Length > 0) { MessageBox.Show(this, Msg, "Execution Errors", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // END if (RetVal.Count > 0) } // END if (RetVal != null) } else { foreach (String Result in CheckerResults) { Msg += Result; } MessageBox.Show(this, Msg, "Compile Failure Information", MessageBoxButtons.OK, MessageBoxIcon.Information); } } // END if (pExecute) } // END if (DOMChecker.CompiledOK) else { SB.AppendLine(""); SB.AppendLine("Failed to Compile."); ResultsTextBox.Text = SB.ToString(); SB.Clear(); SB = null; } // END else of [if (DOMChecker.CompiledOK)] DOMChecker = null; } // END private void DoCompile(Boolean pExecute)