-
2005-11-04
如何在.NET中实现脚本引擎 (CodeDom篇) - [程序人生]
.NET 本身提供了强大的脚本引擎,可以直接使用.NET CLR的任何编程语言作为脚本语言,如VB.NET、C#、JScript, J#等等。使用脚本引擎,我们可以动态生成任意表达式、或动态导入任意脚本文件,并在任意时候执行。
经实践发现,我们可以使用至少两种不同的方式在.NET中使用脚本引擎:VsaEngine和CodeDom。
其实,CodeDom不能算是真正的脚本引擎,它实际上是编译器。但是我们完全可以利用CodeDom来模拟脚本引擎。
使用Emit方法也能达到动态生成可执行代码的目的,而且Emit生成的代码不需要编译,因此速度更快。但是Emit插入的实际上是汇编代码,不能算是脚本语言。
本文介绍如何以CodeDom方式来动态生成可执行代码。
如何在.NET中实现脚本引擎 (CodeDom篇) 沐枫网志 http://ly4cn.cnblogs.com
1. 构造一个编译器
设置编译参数
编译参数需要在CompilerParameters设置:
CompilerOptions 用于设置编译器命令行参数
IncludeDebugInformation 用于指示是否在内存在生成Assembly
GenerateInMemory 用于指示是否在内存在生成Assembly
GenerateExecutable 用于指示生成的Assembly类型是exe还是dll
OutputAssembly 用于指示生成的程序文件名(仅在GenerateInMemory为false的情况)
ReferencedAssemblies 用于添加引用Assembly
例如:
theParameters.ReferencedAssemblies.Add("System.dll");
创建指定语言的编译器
编译需要由指定语言的CodeDomProvider生成。
这里列举一些.NET的CodeDomProvider:
vb.net Microsoft.VisualBasic.VBCodeProvider
C# Microsoft.CSharp.CSharpCodeProvider
jscript Microsoft.JScript.JScriptCodeProvider
J# Microsoft.VJSharp.VJSharpCodeProvider
以C#为例,要创建C#编译器,代码如下:
ICodeCompiler compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
下面是完整的创建编译器的例子:
/**//// <summary>
/// 创建相应脚本语言的编译器
/// </summary>
private void createCompiler(string strLanguage, bool debugMode, string strAssemblyFileName)
{
this.theParameters = new CompilerParameters();
this.theParameters.OutputAssembly = System.IO.Path.Combine(System.IO.Path.GetTempPath(), strAssemblyFileName + ".dll");
this.theParameters.GenerateExecutable = false;
this.theParameters.GenerateInMemory = true;
if(debugMode)
{
this.theParameters.IncludeDebugInformation = true;
this.theParameters.CompilerOptions += "/define:TRACE=1 /define:DEBUG=1 ";
}
else
{
this.theParameters.IncludeDebugInformation = false;
this.theParameters.CompilerOptions += "/define:TRACE=1 ";
}
AddReference("System.dll");
AddReference("System.Data.dll");
AddReference("System.Xml.dll");
strLanguage = strLanguage.ToLower();
CodeDomProvider theProvider;
if("visualbasic" == strLanguage || "vb" == strLanguage)
{
theProvider = new Microsoft.VisualBasic.VBCodeProvider();
if(debugMode)
theParameters.CompilerOptions += "/debug:full /optimize- /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
else
theParameters.CompilerOptions += "/optimize /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
AddReference("Microsoft.VisualBasic.dll");
}
else if("jscript" == strLanguage || "js" == strLanguage)
{
theProvider = new Microsoft.JScript.JScriptCodeProvider();
AddReference("Microsoft.JScript.dll");
}
else if("csharp" == strLanguage || "cs" == strLanguage || "c#" == strLanguage)
{
theProvider = new Microsoft.CSharp.CSharpCodeProvider();
if(!debugMode)
theParameters.CompilerOptions += "/optimize ";
-
2005-04-11
int 与 byte[] 的相互转换 - [程序人生]
关于 int 与 byte[] 的相互转换,Mattias Sjogren 介绍了3种方法。请参见 《将Integer转换成Byte Array》。其实应该还有不少方法。在这里,我归纳了包括Mattias Sjogren在内的4种方法。
1. 最普通的方法
从byte[] 到 uint
b = new byte[] {0xfe,0x5a,0x11,0xfa};
u = (uint)(b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24);
从int 到 byte[]
b[0] = (byte)(u);
b[1] = (byte)(u >> 8);
b[2] = (byte)(u >> 16);
b[3] = (byte)(u >> 24);
2. 使用 BitConverter (强力推荐)
从int 到byte[]
byte[] b = BitConverter.GetBytes(
0xba5eba11 );
//{0x11,0xba,0x5e,0xba}
从byte[]到int
uint u = BitConverter.ToUInt32(
new byte[] {0xfe, 0x5a, 0x11,
0xfa},0 ); // 0xfa115afe
3. Unsafe代码 (虽然简单,但需要更改编译选项)
unsafe {// 从int 到byte[] fixed ( byte* pb = b )
// 从byte[] 到 int u = *((uint*)pb);}
4. 使用Marshal类
IntPtr ptr = Marshal.AllocHGlobal(4); // 要分配非托管内存
byte[] b= new byte[4]{1,2,3,4};
//从byte[] 到 int
Marshal.Copy(b, 0, ptr, 4);
int u = Marshal.ReadInt32(ptr);
//从int 到byte[]
Marshal.WriteInt32(ptr, u);
Marshal.Copy(ptr,b,0,4);
Marshal.FreeHGlobal(ptr); // 最后要记得释放内存
使用第4种看起来比较麻烦,实际上,如果想把结构(struct)类型转换成byte[],则第4种是相当方便的。例如:
int len = Marshal.Sizeof(typeof(MyStruct));
MyStruct o;
byte[] arr = new byte[len];//{...};
IntPtr ptr = Marshal.AllocHGlobal(len);
try
{
// 从byte[] 到struct MyStruct
Marshal.Copy(arr, index, ptr, Math.Min(length, arr.Length - index));
o = (MyStruct)Marshal.PtrToStructure(ptr, typeof(MyStruct));
// 从struct MyStruct 到 byte[]
Marshal.StructureToPtr(o, ptr, true); // 使用时要注意fDeleteOld参数
Marshal.Copy(ptr, arr, 0, len);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return o;
-
2004-04-08
解除Managed C++ DLL对kernel32.dll的引用 - [开发日志]
[解除Managed C++ DLL对kernel32.dll的引用]
用c++.net2002生成的managed dll,在默认情况下它会自动引用kernel32.dll,并且生成的dll也比较大。使用ildasm查看,可以看到dll多了__DllMainStartUp@12函数。而使用C#或VB.NET生成的DLL是不会这样的。
为什么会这样?原因很简单,为了兼容非managed代码。假如在程序在使用到非Managed C++的new/delete以及析构函数,全局或静态变量等,就得需要C/C++运行库和启动代码的支持。
最新的MSDN认为,这种方法有可能使映象在某种情况下无法正常运行。
因此,当没有使用到C/C++运行库,就可以去掉这些东西,以使生成的DLL更小,更纯。方法就是在链接器选项加入“/NOENTRY /INCLUDE:__DllMainStartUp@12”。
在C++.NET2003中,向导在生成Managed DLL时,默认情况是不使用C/C++运行库,并替换掉启动代码。此时如果程序中要使用C/C++运行库,就可能在运行时会出错。
MSDN的解决方法是:
1. 链接器选项加入“/NOENTRY /INCLUDE:__DllMainStartUp@12”。
2. C++编译选项命令行中去掉“/zl”。
3. “输入->附加依赖项”去掉“nochkclr.obj”,以及加入对应的msvcrt.lib。
4. 必须在此Dll中首先调用CRT的起动代码(__crt_dll_initialize();),并在Dll不用的时候,调用结束代码(__crt_dll_terminate();)。如果有多个这样的Dll,则要保证每个Dll都必须调用至少一次。
5. 根据Dll形态不同(如COM,含有Export的Dll),可以用不同的方法来调用CRT起动和终结函数,具体请参考MSDN的“Converting Managed Extensions for C++ Projects from Pure Intermediate Language to Mixed Mode”
-
[.NET的序列化]
.NET反序列化是一个比较奇怪的过程.
它是按广度优先的顺序构造的. 而且, .NET1.0和.NET1.1有着明显的区别.
首先, 它先构造顶层对象(调用反序列化构造函数), 在构造时, 引用类型的子对象此时是没有意义的. 在.NET1.0中, 引用类型的子对象空间被分配, 但所有的成员数据没有意义; 而在.NET1.1中, 所有的引用类型的子对象的值为Nothing. 如果子对象是结构类型, 则会优先反序列化.
在上层对象的反序列化构造函数完成后, 再继续调用引用类型的子对象的反序列化构造函数.
-
2004-04-08
.NET Framework 接收BeforeNavigate2事件BUG的替代方法 - [开发日志]
[.NET Framework 接收BeforeNavigate2事件BUG的替代方法]
这是一篇微软网络上找到的文章,现在也许不适用了,但如果仍用.NET Framework 1.0,则只有这个方法可以解决这个问题了。
Liju Thomas [@online.microsoft.com]
Regarding BeforeNavigate2 it a bug as mentioned in the KB article.
But you can connect directly to IWebBrowserEvents (NOT IWebBrowserEvents2)
and sink to the BeforeNavigate event.
sample code:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Data;
using SHDocVw;
namespace InetTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form, DWebBrowserEvents
{
private AxSHDocVw.AxWebBrowser axWebBrowser1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
private UCOMIConnectionPoint icp;
private int cookie = -1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Object obj;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Sink to webbrowser events
UCOMIConnectionPointContainer icpc =
(UCOMIConnectionPointContainer)axWebBrowser1.GetOcx();
Guid g = typeof(DWebBrowserEvents).GUID;
icpc.FindConnectionPoint(ref g, out icp);
icp.Advise(this, out cookie);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing
{
if( disposing
{
// Release event sink
if (-1 != cookie) icp.Unadvise(cookie);
cookie = -1;
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing ;
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new
System.Resources.ResourceManager(typeof(Form1));
this.axWebBrowser1 = new AxSHDocVw.AxWebBrowser();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.axWebBrowser1)).BeginInit()
;
this.SuspendLayout();
//
// axWebBrowser1
//
this.axWebBrowser1.Enabled = true;
this.axWebBrowser1.Location = new System.Drawing.Point(16, 8);
this.axWebBrowser1.OcxState =
((System.Windows.Forms.AxHost.State)(resources.GetObject("axWebBrowser1.OcxS
tate"));
this.axWebBrowser1.Size = new System.Drawing.Size(456, 208);
this.axWebBrowser1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(16, 232);
this.button1.Name = "button1";
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// label1
//
this.label1.Location = new System.Drawing.Point(112, 232);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(344, 23);
this.label1.TabIndex = 2;
this.label1.Text = "label1";
//
// Form1
//
-
2004-04-08
SMS PDU模式,数据格式和长度计算 - [开发日志]
[SMS PDU模式,数据格式和长度计算]
手机短信发送时,使用SMS PDU模式,数据格式和长度计算方法:
UDL 为UD的字节长度
如:7bit数据 "hello"
UDL = 05, UD = E8 32 9B FD 06
又如:16bit数据 "abc"
UDL = 06, UD = 00 61 00 62 00 63
CSMS 长度,为去掉SCA后的字节长度
如:00 11 00 07 81 21 43 56 F7 00 00 AA 05 E8 32 9B FD 06
其中SCA 为00,一个字节
AT+CSMS=17
又如:07 91 94 71 01 67 00 00 11 00 07 81 21 43 65 F7 00 F6 AA 05 68 65 6C 6C 6F
其中SCA 为07 91 94 71 01 67 00 00 (+491710760000),8个字节
AT+CSMS=17
数据格式有3种:
7bit,8bit,16bit。
其中7bit采用GSM字符集,8bit采用ASCII字符集,16bit采用Unicode字符集。
字节序采用网络字节顺序。
-
2004-04-08
VsaEngine脚本无法执行新的代码 - [开发日志]
[VsaEngine脚本无法执行新的代码]
.NET1.0 升级到 .NET1.1时,执行VsaEngine.Run后,脚本更新也无法执行新的代码的问题的替代解决方案。
经测试,发现,VsaEngine可以更新源码,并能正确编译出目标代码。但是一旦执行了目标代码,VsaEngine内存中的目标代码将再也不更新,导致了上面的问题。而.NET1.0中可以正常更新。
无奈,使用替代方案来完成:即使用CodeDom.Compiler来编译代码,并直接使用编译后的Assembly来获取函数信息或执行相关的函数。
潜在的问题:
潜在的问题是,编译后的Assembly无法卸载,除非进程关闭。这样,只要编译并使用过Assembly一次,Assembly将永驻内存。这个问题同样也存在于.NET1.0的VsaEngine。可能就是基于这个原因,.NET1.1的VsaEngine才改变策略,使得Assembly不再更新,也就不会出现Assembly越来越多的驻留内存的情况。而这恰好不是我们所希望的。
.NET中,只有AppDomain才能被卸载,AppDomain中的所有Assembly也会跟着被卸载。但是一个AppDomain中的Assembly不能被其它AppDomain访问,除非它是可以被跨AppDomain访问的(但是这样的Assembly也就不会与AppDomain一起被卸载)。
-
[搬家了]
今天找到了 yourblog,发现这边访问特别快,以前的我登记的blog网站实在慢得难受!
搬家了,只是,把以前的文章都转过来,也有些累,不过工作总是要做的。[face01] 不过,转文章时,不打算把文章以前发表的日期也保留,最多可能在正文中注一下,必竟也是一种新的审视吧(其实是以前文章太少了)。
昨天到北京出差,这回估计要到五一才能回去了。想不到家里下雨,北方却艳阳高照,比家乡热了好多!总算这回办事处宽带费交上了,可以上网,否则不闷死才怪。 -
2004-04-08
C++实现Sealed类 - [开发日志]
[C++实现Sealed类]
今天看到《软件研发5》有一篇译自CUJ的文章“使类不可继承”,方法很好,但有几个毛病:
1. 正如译者说的,还是有办法进行继承,虽然方法有点变态
2. 最主要的毛病却是,这种办法会造成运行时的开销。因为至少会增加VTABLE指针。
所以,针对以上两个问题,作了改动,如下:
#ifdef _DEBUG
namespace internalSealed
{
template<typename T>
class Class_Is_Sealed
{
protected:
Class_Is_Sealed(){};
};
};
template<typename T>
class Sealed: private virtual internalSealed::Class_Is_Sealed<T>
{
friend typename T;
};
#else
template<typename T>
class Sealed
{
};
#endif
这样子,在Debug方式下,只要一个类从Sealed<T>继承,就不可再被继承了。
同时,在Release方式下,因为不再检查是否可以被继承,因而不产生开销(空类会被编译器优化掉)。
使用例子:
#include "Sealed.h"
class test: Sealed<test>
{
public:
int print()
{
return 1;
}
};
class ttt: public test //, Sealed<test>, Sealed<ttt>
{
public:
int print()
{
return 2;
}
};
int main(int argc, char* argv[])
{
test t;
printf("%d\n", t.print());
ttt t1;
printf("%d\n", t1.print());
return 0;
}
共1页 1






