请教C#中代码动态编译Microsoft.CSharp.Compiler.Compile(...)出错的问题

yean 2005-09-12 09:33:38
以上问题出错
具体我是参考

有一位做ASP的朋友问我:“在ASP.NET中的程序如何进行动态的编译,就像在ASP中一样,由自己通过程序生成应用程序文件,然后直接运行。”我不禁失笑,可转念一想,在一个既要灵活又要高效率的系统中我们确实需要遵循某些规则来动态生成一些程序,然后由新的程序来负担新的任务,这样运行的效率才比较高。当然也可以使用不同的配置信息利用同一个程序完成,但是这就好像使用Html和使用应用程序作为主要浏览格式的网站一样,当然是Html浏览的速度更快,效率更高。于是我回答他说直接把代码写在aspx文件中,不使用Codebehind技术,他想了很长时间说,不行,那样代码不就让人给看见了。既要保证代码的安全又要满足高效率,于是我开始认真思考这个问题,觉得有深入研究的必要,经过摸索,现将一些心得体会公布于众。大家都知道,在ASP.NET中利用CodeBehind技术编写好的文件必须先通过编译,然后才能被执行。我们一般是在VS.NET中或者命令行下进行编译,那么现在的问题就是在程序中进行编译,也就是自己通过做好的规则动态生成一些程序源文件、然后对其进行编译。查看.Net Framework中提供的类库,发现了Microsoft.Csharp这个命名空间,其中有一个类Compiler。要调用这个类库必须先在引用中添加D:\WINNT\Microsoft.NET\Framework\v1.0.3705\cscompmgd.dll这个文件,这是我机器上的路径,各位根据自己机器所在路径设置吧。仔细研究Compiler这个类:不需要初始化,直接可使用Compile方法,其中包含了5个参数。string[] sourceTexts 源代码数组 其中每一个值包含一个文件中的源码string[] sourceTextNames 源文件名数组 其中每一个值包含了一个文件名和上面的源代码数组中的值一一对应。
string target 输出的文件名

string[] imports 引用类库数组 也就是我们在VS.net中引用的那些类库的文件路径,一般都是dll文件,其中一个值表示一个引用文件。

IDictionary options 参数设置,实际上这也是一个数组,可以包含多个参数,具体的参数我在下面的部分会讲到。

实际这个Compile这个方法要求我们输入这些条件:

1、源代码——这个是必须的,没有源代码没法编译,那么源代码要求我们从程序文件中读取出来。

2、源文件的名称,和读取出的源代码一一对应。

3、编译输出的文件名,也可不填

4、引用的类库文件,如System.dll,System.data.dll这些都是经常会用到的。

5、其他参数设置

第一步:创建一个类及调用方法

首先我们创建一个Class

using System;

using System.IO;

using System.Text;

using Microsoft.CSharp;

namespace MyTest

{

private String[] filelist; //文件列表

private String[] cscode; //源代码

private String[] filename; //文件名

private String[] import; //引用类库

public class MyComplier()

{

//这里我做了一个简单的方法,要求用户输入两个参数,一个是文件夹的绝对路径

另外一个是输出的文件路径

public CompilerError[] MyCompile(String realpath,String output)

{

//……………………

}

}

}

那么下面我们会一步步来讲解如何实现这个方法。 第二步:读取程序文件列表

由于使用CodeBehind,那么实际上要编译的也就是扩展名为CS的文件。首先读取文件列表,制作一个方法用来读取目录下的文件路径:通过用户输入的绝对路径对文件进行整理搜索。

public String[] GetFileList(String realpath)//输入绝对路径

{

return System.IO.Directory.GetFiles(realpath,”*.cs”);//输入搜索条件

}

本方法将要编译的真实目录传递进入,然后返回目录下扩展名为CS的文件路径列表。那么读取文件的列表将会为文件中代码的获取及文件名的获取做准备。

第三步:读取程序文件内容

//这里我们调用了第二步建立的方法来获取文件列表

filelist= GetFileList(realpath); //获取文件路径列表

//根据文件的数量创建数组

cscode=new String[filelist.Length]; //建立源代码数组

String[] filename=new String[filelist.Length];//建立源文件名数组

//通过循环获取每个文件的信息每增加到对应的数组中

For (int I=0;I

{

String filepath= filelist. GetValue(i).ToString();//获取单个文件路径

cscode[I]= ReadTxtFile(filepath, Encoding.GetEncoding("GB2312"));//读取源文件中的代码、这里的编码为GB2312,也可设置为其他,这里调用的是我们下面创建的一个方法

filename[I]= System.IO.Path.GetFileName(filepath);//获取文件名

}

//读取文本文件内容

public String ReadTxtFile(String path,Encoding encode)

{

String str="";

try

{

if (File.Exists(path))

{

StreamReader sr=new StreamReader(path,encode);

str=sr.ReadToEnd();

sr.Close();

}

}

catch(IOException ex)

{Console.Write(ex.ToString());}

return str;

}

经过第二步之后我们已经满足了Compile方法所需的两个参数了,那么接下来将是对文件的输出及引用的一些配置。 

第四步:配置输出文件名及路径

此处可根据你的需要,设置文件和路径,例如:E:\test\bin\test.dll String output=” E:\\test\\bin\\test.dll”;

这里我们根据用户输入的路径进行操作。

第五步:配置引用的类库文件

那么只要是你在程序引用到的类库,在此处都必须加入到数组中。

import=new String[10];//定义一个数组,数组长度根据你所需要引用的文件数量决定

import[0]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\System.dll";

import[1]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\IEHost.dll";

import[2]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\System.Data.dll";

import[3]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\System.Drawing.dll";

import[4]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\System.Web.dll";

import[5]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\System.Xml.dll";

import[6]="D:\\WINNT\\Microsoft.NET\\Framework\\v1.0.3705\\cscompmgd.dll";

上面的是我们经常用到的一些类库,除此之外,你自己编写的一些类库,如果也被引用到了,那么也必须增加到数组中。 第六步:进行属性参数配置

这里系统提供给了我们一个接口:Idictionary,这个接口可使用以下几个类来实现。

ListDictionary 使用单链接列表实现 IDictionary。建议用于通常包含 10 个或 10 个以下项的集合。

HybridDictionary在集合较小时,使用 ListDictionary 来实现 IDictionary,然后当集合变大时,切换到 Hashtable。

还有其他一些,我感觉用不上,这里就不讲了。那么,具体来看看:

System.Collections.IDictionary configs=new System.Collections.Specialized.ListDictionary();//实现这个接口

configs.Add("target","library"); //配置输出文件类型为库文件,这个参数是必须的

其中”target”为文件的格式属性,”library”为属性的值。

其他属性有:

addmodule 导入元数据 要包含在此程序集内的模块列表。模块名必须用竖线或管道字符分隔。值的类型必须是 String。

baseaddress 库的基址。值的类型必须是 Uint32

bugreport 产生错误报告文件。值的类型必须是 String。

checked 检查整数算法 设置默认表达式计算为选中(或未选中)。值的类型必须是 Boolean。

Debug发出调试信息 值的类型必须是 Boolean。不能指定“全部”或“pdbonly”。

Lib 指定程序集引用位置 查找模块和引用的程序集时要搜索的附加路径。值的类型必须是 String。

Unsafe启用不安全模式 允许不安全构造。值的类型必须是 Boolean

win32icon自动生成的 Win32 资源的 Win32 图标。值的类型必须是String

这里讲出主要的一些参数,还有一些由于用得比较少,那么就不一一列出了。

第七步:程序编译

CompilerError[] ce= Compiler.Compile(cscode,filename, output,import, configs);

那么实际上我们最后的编译也就是调用这个方法,此方法将会返回一个包含出错信息的数组。其中记录了编译过程中的出错信息,如果有错误信息返回,则编译无法通过。

最后我们将第一步中的方法整合:

public CompilerError[] MyCompile(String realpath,String output)

{

//……………………

通过上面的方法获取到参数,最后执行编译。

CompilerError[] ce= Compiler.Compile(cscode,filename, output,import, configs);

return ce;

}

到这里,基本上整个编译过程就完成了,大家可以在Aspx页面中调用或者在其他的类中调用,编译出来的效果和VS.net比起来一点不差,而且速度也非常快。大家甚至可以构建一个基于Web的简易的集成开发环境,当然这是后话了。
...全文
579 6 打赏 收藏 举报
写回复
6 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
yxred 2005-10-14
同上,太长了。
先列出提纲让大家有个总体概念
  • 打赏
  • 举报
回复
zhy0101 2005-10-14
太长了。
  • 打赏
  • 举报
回复
chequan 2005-10-14
我很想知道怎么解决,帮你顶!
  • 打赏
  • 举报
回复
yean 2005-09-14
倒怎么没人理我
  • 打赏
  • 举报
回复
yean 2005-09-14
要沉了自己顶一下..............
  • 打赏
  • 举报
回复
yean 2005-09-12
出错位置是Microsoft.CSharp.CompilerError[] ce=Compiler.Compile(_cscode,_filename, _outputpath,_import, configs);
具体出错提示是“是无法在设置线程模式后对其加以更改”
我把代码也贴上来请各位师兄指点俺

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Text;
using Microsoft.CSharp;
using System.Runtime.InteropServices;
namespace WindowsApplication2
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private String _physicspath = @"C:\\Inetpub\\wwwroot\\Web1111";//文件夹的路径
private String _outputpath = @"C:\Inetpub\wwwroot\Web1111\bin\web1111.dll";//输出文件的路径

private String[] _filelist; //文件列表
private String[] _cscode; //源码
private String[] _filename; //文件名
private String[] _import; //引用
private System.Collections.IDictionary configs = null;
private System.Windows.Forms.MainMenu mainMenu1;//编译属性参数配置
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();

//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
}

/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(208, 96);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.button1);
this.Menu = this.mainMenu1;
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion
public Microsoft.CSharp.CompilerError[] ExCompiler()
{
Fill_cscode();
Fill_import();
Fill_configs();
Microsoft.CSharp.CompilerError[] ce=Compiler.Compile(_cscode,_filename, _outputpath,_import, configs);
return ce;
}
private String[] Fill_filelist()
{
return System.IO.Directory.GetFiles(_physicspath,"*.cs");
}
private void Fill_cscode()
{
_filelist= Fill_filelist();
_cscode=new String[_filelist.Length];
_filename=new String[_filelist.Length];
for(int i=0;i<_filelist.Length;i++)
{
String filepath= _filelist.GetValue(i).ToString();//获取文件路径
_cscode[i]= ReadCodeFile(filepath, Encoding.GetEncoding("utf-8"));
_filename[i]= System.IO.Path.GetFileName(filepath);
}
}
private void Fill_import()
{
_import=new String[10];//数组长度视引用文件数量
_import[0]=@"C:\\WINDOWS\\Microsoft.NET\Framework\\v1.1.4322\\System.dll";
_import[1]=@"C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\System.Web.dll";
_import[2]=@"C:\\WINDOWS\\Microsoft.NET\Framework\\v1.1.4322\\System.Collections.dll";
_import[3]=@"C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\System.ComponentModel.dll";
_import[4]=@"C:\\WINDOWS\\Microsoft.NET\Framework\\v1.1.4322\\System.Data.dll";
_import[5]=@"C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\System.Drawing.dll";
_import[6]=@"C:\\WINDOWS\\Microsoft.NET\Framework\\v1.1.4322\\System.Web.SessionState.dll";
_import[7]=@"C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\System.Web.UI.dll";
_import[8]=@"C:\\WINDOWS\\Microsoft.NET\Framework\\v1.1.4322\\System.Web.UI.WebControls.dll";
_import[9]=@"C:\\WINDOWS\\Microsoft.NET\\Framework\\v1.1.4322\\System.Web.UI.HtmlControls.dll";
}
private void Fill_configs()
{
configs=new System.Collections.Specialized.ListDictionary();
configs.Add("target","Unsafe");
}
private String ReadCodeFile(String path,Encoding encode)
{
String content=String.Empty;
try
{
if (File.Exists(path))
{
StreamReader sr=new StreamReader(path,encode);
content=sr.ReadToEnd();
sr.Close();
}
}
catch(IOException ex)
{
Console.Write(ex.ToString());
}
return content;
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
Microsoft.CSharp.CompilerError[] uu=ExCompiler();
}
}
}
  • 打赏
  • 举报
回复
相关推荐
发帖
C#

10.8w+

社区成员

.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
帖子事件
创建了帖子
2005-09-12 09:33
社区公告

让您成为最强悍的C#开发者