using System; using System.ComponentModel; using System.ComponentModel.Design; using System.ComponentModel.Design.Serialization; using System.Collections; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.Serialization.Formatters.Soap; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Windows.Forms; using System.Xml; using System.CodeDom.Compiler; using System.CodeDom; using System.Drawing; using Microsoft.CSharp; using Microsoft.VisualBasic; namespace Loader { /// /// Inherits from CodeDomDesignerLoader. It can generate C# or VB code /// for a HostSurface. This loader does not support parsing a /// C# or VB file. /// public class CodeDomHostLoader : CodeDomDesignerLoader { CSharpCodeProvider _csCodeProvider = new CSharpCodeProvider(); CodeCompileUnit codeCompileUnit = null; CodeGen cg = null; TypeResolutionService _trs = null; private string executable; private Process run; public CodeDomHostLoader() { _trs = new TypeResolutionService(); } protected override ITypeResolutionService TypeResolutionService { get { return _trs; } } protected override CodeDomProvider CodeDomProvider { get { return _csCodeProvider; } } /// /// Bootstrap method - loads a blank Form /// /// protected override CodeCompileUnit Parse() { CodeCompileUnit ccu = null; DesignSurface ds = new DesignSurface(); ds.BeginLoad(typeof(Form)); IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost)); idh.RootComponent.Site.Name = "Form1"; cg = new CodeGen(); ccu = cg.GetCodeCompileUnit(idh); AssemblyName[] names = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); for (int i = 0; i < names.Length; i++) { Assembly assembly = Assembly.Load(names[i]); ccu.ReferencedAssemblies.Add(assembly.Location); } codeCompileUnit = ccu; return ccu; } /// /// When the Loader is Flushed this method is called. The base class /// (CodeDomDesignerLoader) creates the CodeCompileUnit. We /// simply cache it and use this when we need to generate code from it. /// To generate the code we use CodeProvider. /// protected override void Write(CodeCompileUnit unit) { codeCompileUnit = unit; } protected override void OnEndLoad(bool successful, ICollection errors) { base.OnEndLoad(successful, errors); if (errors != null) { IEnumerator ie = errors.GetEnumerator(); while (ie.MoveNext()) System.Diagnostics.Trace.WriteLine(ie.Current.ToString()); } } #region Public methods /// /// Flushes the host and returns the updated CodeCompileUnit /// /// public CodeCompileUnit GetCodeCompileUnit() { Flush(); return codeCompileUnit; } /// /// This method writes out the contents of our designer in C# and VB. /// It generates code from our codeCompileUnit using CodeRpovider /// public string GetCode(string context) { Flush(); CodeGeneratorOptions o = new CodeGeneratorOptions(); o.BlankLinesBetweenMembers = true; o.BracingStyle = "C"; o.ElseOnClosing = false; o.IndentString = " "; if (context == "C#") { StringWriter swCS = new StringWriter(); CSharpCodeProvider cs = new CSharpCodeProvider(); cs.GenerateCodeFromCompileUnit(codeCompileUnit, swCS, o); string code = swCS.ToString(); swCS.Close(); return code; } else if (context == "VB") { StringWriter swVB = new StringWriter(); VBCodeProvider vb = new VBCodeProvider(); vb.GenerateCodeFromCompileUnit(codeCompileUnit, swVB, o); string code = swVB.ToString(); swVB.Close(); return code; } return String.Empty; } #endregion #region Build and Run /// /// Called when we want to build an executable. Returns true if we succeeded. /// public bool Build() { Flush(); // If we haven't already chosen a spot to write the executable to, // do so now. if (executable == null) { SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = "exe"; dlg.Filter = "Executables|*.exe"; if (dlg.ShowDialog() == DialogResult.OK) { executable = dlg.FileName; } } if (executable != null) { // We need to collect the parameters that our compiler will use. CompilerParameters cp = new CompilerParameters(); AssemblyName[] assemblyNames = Assembly.GetEntryAssembly().GetReferencedAssemblies(); foreach (AssemblyName an in assemblyNames) { Assembly assembly = Assembly.Load(an); cp.ReferencedAssemblies.Add(assembly.Location); } cp.GenerateExecutable = true; cp.OutputAssembly = executable; // Remember our main class is not Form, but Form1 (or whatever the user calls it)! cp.MainClass = "DesignerHostSample." + this.LoaderHost.RootComponent.Site.Name; CSharpCodeProvider cc = new CSharpCodeProvider(); CompilerResults cr = cc.CompileAssemblyFromDom(cp, codeCompileUnit); if (cr.Errors.HasErrors) { string errors = ""; foreach (CompilerError error in cr.Errors) { errors += error.ErrorText + "\n"; } MessageBox.Show(errors, "Errors during compile."); } return !cr.Errors.HasErrors; } return false; } /// /// Here we build the executable and then run it. We make sure to not start /// two of the same process. /// public void Run() { if ((run == null) || (run.HasExited)) { if (Build()) { run = new Process(); run.StartInfo.FileName = executable; run.Start(); } } } /// /// Just in case the red X in the upper right isn't good enough, /// we can kill our process here. /// public void Stop() { if ((run != null) && (!run.HasExited)) { run.Kill(); } } #endregion }// class }// namespace