问题描述
我使用标准的C#WinForms设计器让用户创建对话框.当用户想要显示对话框时,我可以使用我自己的CodeDomDesignerLoader子类(称为FormEditorCodeDomDesignerLoader)将其编译为Assembly文件(DLL).
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.
在FormEditorCodeDomDesignerLoader中创建CodeCompileUnit的代码如下:
The code that creates the CodeCompileUnit in the FormEditorCodeDomDesignerLoader looks like this:
受保护的重写CodeCompileUnit Parse()
{
DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof(Form));
IDesignerHost idh =(IDesignerHost)ds.GetService(typeof(IDesignerHost));
protected override CodeCompileUnit Parse()
{
DesignSurface ds = new DesignSurface();
ds.BeginLoad(typeof (Form));
IDesignerHost idh = (IDesignerHost)ds.GetService(typeof(IDesignerHost));
返回CreateCCU(idh);
}
return CreateCCU (idh);
}
私有CodeCompileUnit CreateCCU(IDesignerHost idh)
{
idh.RootComponent.Site.Name ="Form1";
private CodeCompileUnit CreateCCU (IDesignerHost idh)
{
idh.RootComponent.Site.Name = "Form1";
cg = new FormEditorCodeGen();
CodeCompileUnit ccu = cg.GetCodeCompileUnit(idh);
cg = new FormEditorCodeGen();
CodeCompileUnit ccu = cg.GetCodeCompileUnit(idh);
AssemblyName []名称= Assembly.GetExecutingAssembly().GetReferencedAssemblies();
for(int i = 0; i< names.Length; i ++)
{
程序集程序集= Assembly.Load(names [i]);
ccu.ReferencedAssemblies.Add(assembly.Location);
}
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;
}
codeCompileUnit = ccu;
return ccu;
}
内部类FormEditorCodeGen
{
私有CodeCompileUnit codeCompileUnit;
私有CodeNamespace ns;
私有CodeTypeDeclaration myDesignerClass = new CodeTypeDeclaration();
私有CodeMemberMethod initializeComponent = new CodeMemberMethod();
私人IDesignerHost主机;
私有IComponent根;
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>
///此函数生成默认的CodeCompileUnit模板
///</summary>
公共CodeCompileUnit GetCodeCompileUnit(IDesignerHost主机)
{
this.host =主机;
IDesignerHost idh =(IDesignerHost)this.host.GetService(typeof(IDesignerHost));
root = idh.RootComponent;
哈希表名称表=新的哈希表(idh.Container.Components.Count);
/// <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();
ns = new CodeNamespace("YetiFormEditor");
myDesignerClass = new CodeTypeDeclaration();
initializeComponent = new CodeMemberMethod();
CodeCompileUnit代码=新的CodeCompileUnit();
CodeCompileUnit code = new CodeCompileUnit();
//导入
ns.Imports.Add(新的CodeNamespaceImport(系统")));
ns.Imports.Add(新的CodeNamespaceImport("System.ComponentModel"));
ns.Imports.Add(新的CodeNamespaceImport("System.Windows.Forms"));
ns.Imports.Add(新的CodeNamespaceImport("AGI.FormEditor"));
code.Namespaces.Add(ns);
myDesignerClass =新的CodeTypeDeclaration(root.Site.Name);
myDesignerClass.BaseTypes.Add(typeof(Form).FullName);
// 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管理器=
nbsp; bsp host.GetService(typeof(IDesignerSerializationManager))作为IDesignerSerializationManager;
IDesignerSerializationManager manager =
host.GetService(typeof(IDesignerSerializationManager)) as IDesignerSerializationManager;
ns.Types.Add(myDesignerClass);
ns.Types.Add(myDesignerClass);
//构造函数
CodeConstructor con =新的CodeConstructor();
// Constructor
CodeConstructor con = new CodeConstructor();
con.Attributes = MemberAttributes.Public;
con.Statements.Add(new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(new CodeThisReferenceExpression(),"InitializeComponent")));
myDesignerClass.Members.Add(con);
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 =新的CodeTypeReference(typeof(void));
myDesignerClass.Members.Add(initializeComponent);
codeCompileUnit =代码;
返回codeCompileUnit;
}
}//班级
// InitializeComponent
initializeComponent.Name = "InitializeComponent";
initializeComponent.Attributes = MemberAttributes.Private;
initializeComponent.ReturnType = new CodeTypeReference(typeof(void));
myDesignerClass.Members.Add(initializeComponent);
codeCompileUnit = code;
return codeCompileUnit;
}
}// class
接受CodeCompileUnit并将其转换为Assembly(DLL)文件的代码如下:
The code that takes the CodeCompileUnit and turns it into an Assembly (DLL) file looks like this:
公共布尔构建(字符串dllFileName)
{
Flush();
public bool Build (string dllFileName)
{
Flush();
//我们需要收集编译器将使用的参数.
CompilerParameters cp = new CompilerParameters();
AssemblyName [] assemblyNames = Assembly.GetEntryAssembly().GetReferencedAssemblies();
// We need to collect the parameters that our compiler will use.
CompilerParameters cp = new CompilerParameters();
AssemblyName[] assemblyNames = Assembly.GetEntryAssembly().GetReferencedAssemblies();
foreach(assemblyNames中的AssemblyName和)
{
]装配装配= Assembly.Load(an);
] cp.ReferencedAssemblies.Add(assembly.Location);
}
foreach (AssemblyName an in assemblyNames)
{
Assembly assembly = Assembly.Load(an);
cp.ReferencedAssemblies.Add(assembly.Location);
}
cp.GenerateExecutable = false; //仅对包含入口点的代码设置为true
cp.OutputAssembly = dllFileName;
cp.GenerateExecutable = false; // only set to true for code that contains an entry point
cp.OutputAssembly = dllFileName;
//记住我们的主类不是Form,而是Form1!
字符串mainClass ="YetiFormEditor.Form1";
cp.MainClass = mainClass;
// Remember our main class is not Form, but Form1!
string mainClass = "YetiFormEditor.Form1";
cp.MainClass = mainClass;
CSharpCodeProvider cc =新的CSharpCodeProvider();
CompilerResults cr = cc.CompileAssemblyFromDom(cp,codeCompileUnit);
CSharpCodeProvider cc = new CSharpCodeProvider();
CompilerResults cr = cc.CompileAssemblyFromDom(cp, codeCompileUnit);
return!cr.Errors.HasErrors;
}
return !cr.Errors.HasErrors;
}
这是我的问题:
Here’s my problem:
每当设计对话框中的任何控件的字符串属性长于200个字符时,生成的DLL中的代码就会包含一个隐式调用,以从DLL程序集的资源中检索此字符串. 并且DLL程序集没有任何字符串资源!尝试调用dllAssembly.CreateInstance("YetiFormEditor.Form1")时,这会导致异常.如果字符串属性为200个字符或更短,则此异常 不会发生,并且对话框创建成功.
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.
我查看了CodeCompileUnit中的C#代码(通过CSharpCodeProvider.GenerateCodeFromCompileUnit()).如果字符串属性为200个字符或更少,我将看到以下内容:
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'';
this.comboBox1.MyProperty = "012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"12345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012345678901234567890123456789";
…但是如果字符串属性超过200个字符,我会看到以下内容:
… but if the string Property is more than 200 characters I’ll see this:
this.comboBox1.MyProperty = resources.GetString("comboBox1.MyProperty");
this.comboBox1.MyProperty = resources.GetString("comboBox1.MyProperty");
考虑到必须超过200个字符的通用属性(例如,图像属性,多行文本编辑的内容等),我无法相信没有其他人遇到过此问题.所以:
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:
是否有一种方法可以使CodeCompileUnit(或CompileAssemblyFromDom()调用)显式包括此生成的代码所依赖的字符串资源,或者有一种方法可以使CodeCompileUnit/CompileAssemblyFromDom()生成长字符串的代码 不需要资源?
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?
谢谢!
推荐答案
最好的问候
这篇关于CodeDom隐式创建资源检索语句;导致无法卸载的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!