Quantcast
Channel: Common Language Runtime Internals and Architecture forum
Viewing all articles
Browse latest Browse all 1710

CodeDom implicitly creating resource retrieval statements; causes unloadable code

$
0
0


I’m using a standard C# WinForms Designer to let the user create a Dialog box.  When the user wants to show the dialog box, I compile it into an Assembly file (a DLL) using my own subclass of CodeDomDesignerLoader, which I call FormEditorCodeDomDesignerLoader.

The code that creates the CodeCompileUnit in the FormEditorCodeDomDesignerLoader looks like this:

        protected override CodeCompileUnit Parse()
        {
            DesignSurface ds = new DesignSurface();
            ds.BeginLoad(typeof (Form));
            IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));

           return CreateCCU (idh);
        }

        private CodeCompileUnit CreateCCU (IDesignerHost idh)
        {
            idh.RootComponent.Site.Name = "Form1";

            cg = new FormEditorCodeGen();
            CodeCompileUnit 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;
        }

       internal class FormEditorCodeGen
       {
         private CodeCompileUnit codeCompileUnit;
         private CodeNamespace ns;
         private CodeTypeDeclaration myDesignerClass = new CodeTypeDeclaration();
         private CodeMemberMethod initializeComponent = new CodeMemberMethod();
         private IDesignerHost host;
         private IComponent root;

         /// <summary>
         /// This function generates the default CodeCompileUnit template
        /// </summary>
         public CodeCompileUnit GetCodeCompileUnit(IDesignerHost host)
        {
            this.host = host;
            IDesignerHost idh = (IDesignerHost)this.host.GetService(typeof(IDesignerHost));
            root = idh.RootComponent;
            Hashtable nametable = new Hashtable(idh.Container.Components.Count);

            ns = new CodeNamespace("YetiFormEditor");
            myDesignerClass = new CodeTypeDeclaration();
            initializeComponent = new CodeMemberMethod();

            CodeCompileUnit code = new CodeCompileUnit();

            // Imports
            ns.Imports.Add(new CodeNamespaceImport("System"));
            ns.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
            ns.Imports.Add(new CodeNamespaceImport("System.Windows.Forms"));
            ns.Imports.Add(new CodeNamespaceImport("AGI.FormEditor"));
            code.Namespaces.Add(ns);
            myDesignerClass = new CodeTypeDeclaration(root.Site.Name);
            myDesignerClass.BaseTypes.Add(typeof(Form).FullName);

            IDesignerSerializationManager manager =
                              host.GetService(typeof(IDesignerSerializationManager)) as IDesignerSerializationManager;

            ns.Types.Add(myDesignerClass);

            // Constructor
            CodeConstructor con = new CodeConstructor();

            con.Attributes = MemberAttributes.Public;
            con.Statements.Add(new CodeMethodInvokeExpression(
           new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "InitializeComponent")));
            myDesignerClass.Members.Add(con);

            // InitializeComponent
            initializeComponent.Name = "InitializeComponent";
            initializeComponent.Attributes = MemberAttributes.Private;
            initializeComponent.ReturnType = new CodeTypeReference(typeof(void));
            myDesignerClass.Members.Add(initializeComponent);
            codeCompileUnit = code;
            return codeCompileUnit;
 }
       }// class

 


The code that takes the CodeCompileUnit and turns it into an Assembly (DLL) file looks like this:

        public bool Build (string dllFileName)
        {
               Flush();

               // 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 = false;  // only set to true for code that contains an entry point
               cp.OutputAssembly = dllFileName;

              // Remember our main class is not Form, but Form1!
             string mainClass = "YetiFormEditor.Form1";
             cp.MainClass = mainClass;

             CSharpCodeProvider cc = new CSharpCodeProvider();
             CompilerResults cr = cc.CompileAssemblyFromDom(cp, codeCompileUnit);

              return !cr.Errors.HasErrors;
        }


Here’s my problem:

Whenever any of the controls within the Dialog box being designed has a string Property that’s longer than 200 characters, the code in the generated DLL contains an implicit call to retrieve this string from the DLL Assembly’s resources – and the DLL Assembly doesn’t HAVE any string resources!  This causes an exception when attempting to call dllAssembly.CreateInstance (“YetiFormEditor.Form1”);.  If the string Property is 200 characters or shorter, this exception does not occur and the dialog box gets created successfully.

I looked at the C# code in the CodeCompileUnit (via CSharpCodeProvider.GenerateCodeFromCompileUnit() ).  If the string Property is 200 characters or less I’ll see this:

this.comboBox1.MyProperty = "012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"12345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012345678901234567890123456789";

… but if the string Property is more than 200 characters I’ll see this:

this.comboBox1.MyProperty = resources.GetString("comboBox1.MyProperty");


I cannot believe no one else has run into this issue, considering how commonplace Properties longer than 200 characters must be (e.g. Image properties, the contents of a multi-line text edit, etc.).  So:

Is there a way to either make the CodeCompileUnit (or the CompileAssemblyFromDom() call) explicitly include the string resources this generated code depends on, or a way to make the CodeCompileUnit/CompileAssemblyFromDom() generate code for long strings that doesn’t require resources?


Thanks!


Viewing all articles
Browse latest Browse all 1710

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>