huyi

BlogJava 首页 新随笔 联系 聚合 管理
  46 Posts :: 5 Stories :: 5 Comments :: 0 Trackbacks

2005年4月1日 #

find dirname -type f -exec egrep "from[ ]{0,}portfolio.*? " {} \;
posted @ 2005-04-14 17:51 HuYi's Blog 阅读(410) | 评论 (0)编辑 收藏

    .NET推崇这样一种思想:相对于框架而言,语言处于从属、次要的地位。CodeDom名称空间中包含的类是这一思想的集中体现。我们可以用CodeDom构造一个树或图,用System.CodeDom名称空间的类填充它,完成后,用对应各种.NET语言的CodeProvider对象将树结构转换成该种语言的代码。要更换一种语言,简单到只需更换一下最后用到的CodeProvider对象。
  
    设想一下,利用这一技术,我们至少能够:
  
    ·查询存储过程的元数据,构造出一个负责参数绑定的类。
  
    ·查询程序集的manifest,构造出一个对每个函数执行单元测试的类。
  
    ·为开发组用到的每一种语言生成样板代码。
  
    ·只需写一份范例代码,就可以让用户自由选择他们要查看哪一种语言的版本。
  
    ·自定义模板语法,经解析后生成任意语言的代码。
  
    ·如果要学习某种不熟悉的语言,可以生成该语言的代码,然后将它与熟悉的语言比较。
  
    一、基本操作
  
    System.CodeDom名称空间包含了许多以语言中立的形式描述常见程序结构的对象,每一种语言的细节则由与该种语言对应的CodeProvider对象负责处理。例如,CodeConditionStatement包含一个TrueStatements集合、一个FalseStatements集合和一个条件属性(Condition attribute),但不涉及条件语句块要用“end if”还是右花括号“}”结束,这部分细节由CodeProvider处理。有了这一层抽象,我们就可以描述待生成的代码结构,然后将它以任意语言的形式输出,却不必斤斤计较于各种与特定语言有关的细节问题。同时,这种抽象也为我们通过程序改变代码的结构带来了方便。例如,当我们发现某个方法需要增加一个参数时,就可以将参数加入到该方法的Parameters集合,根本无须改动已生成的代码逻辑。
  
    我们在本文中要用到的大部分对象来自System.CodeDom名称空间,其余对象主要来自各个与特定语言有关的名称空间,例如Microsoft.CSharp名称空间、Microsoft.VisualBasic名称空间、Microsoft.JScript名称空间和Microsoft.VJSharp名称空间。所有这些面向特定语言的名称空间都包含各自的CodeProvider对象。最后,System.CodeDom.Complier名称空间定义ICodeGenerator接口,后者用来把生成的代码输出到一个TextWriter对象。
  
    如果我们只要生成一些用于插件或宏的代码片断,可以利用CodeGenerator从Statement、Expression、Type等生成代码。反之,如果我们要生成的是一个完整的文件,则必须从CodeNameSpace对象入手。在本文的例子中,我们将从一个名称空间开始,示范如何加入import语句、声明类、声明方法、声明变量、实现一个循环结构、索引一个数组,最后,我们将这些技术结合起来,得到一个大家都熟悉的程序。
  
    1.1 初始化名称空间
  
    初始化名称空间的代码类似下面这种形式:
  
  private CodeNameSpace InitializeNameSpace(string Name)
  {
   // 初始化CodeNameSpace变量,指定名称空间的名称
   CodeNameSpace CurrentNameSpace = new CodeNamespace (Name);
   // 将一些名称空间加入到要导入的名称空间集合。
   // 各种语言如何导入名称空间的细节由每种语言对应
   // 的CodeProvider分别处理。
   CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System"));
   CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System.Text"));
   return CurrentNameSpace;
  }
  
  
  
    这段代码定义了一个新的名称空间,并导入System和System.Text名称空间。
  
    1.2 创建类
  
    声明一个新类的代码类似下面这种形式:
  
  private CodeTypeDeclaration CreateClass (string Name)
  {
   // 新建一个CodeTypeDeclaration对象,指定要创建的类的名称
   CodeTypeDeclaration ctd = new CodeTypeDeclaration (Name);
   // 指定这个CodeType是一个类,而不是一个枚举变量或struct
   ctd.IsClass = true;
   // 这个类的访问类型是public
   ctd.Attributes = MemberAttributes.Public;
   // 返回新创建的类
   return ctd;
  }
  
  
  
    CreateClass函数新建一个指定名称的类,做好为该类植入方法、属性、事件的准备。
  
    1.3 创建方法
  
    声明一个新函数的代码类似下面这种形式:
  
  private CodeEntryPointMethod CreateMethod()
  {
   // 创建一个方法
   CodeEntryPointMethod method = new CodeEntryPointMethod();
   // 指定该方法的修饰符:public,static
   method.Attributes = MemberAttributes.Public |
   MemberAttributes.Static;
   // 返回新创建的方法
   return method;
  }
  
  
  
    本例创建了一个CodeEntryPointMethod对象。CodeEntryPointMethod对象类似于CodeMemberMethod对象,两者的不同之处在于,CodeProvider会将CodeEntryPointMethod代表的方法作为类的入口点调用,例如作为Sub Main或void main等。对于CodeEntryPointMethod对象,方法的名称默认为Main;对于CodeMemberMethod,方法的名称必须显式指定。
  
    1.4 声明变量
  
    声明一个变量的代码类似下面这种形式:
  
  private CodeVariableDeclarationStatement
   DeclareVariables(System.Type DataType,
   string Name)
  {
   // 为将要创建的变量类型创建一个CodeTypeReference对象,
   // 这使得我们不必去关注该类数据在特定语言环境中的
   // 与数据类型有关的细节问题。
   CodeTypeReference tr = new CodeTypeReference (DataType );
   // CodeVariableDeclarationStatement对象使得我们不必纠缠于
   // 与特定语言有关的下列细节:在该语言的变量声明语句中,
   // 应该是数据类型在前,还是变量名称在前;声明变量时是
   // 否要用到Dim之类的关键词.
   CodeVariableDeclarationStatement Declaration =
   new CodeVariableDeclarationStatement(tr, Name);
   // CodeObjectCreateExpression负责处理所有调用构造器的细节。
   // 大多数情况下应该是new,但有时要使用New。但不管怎样,
   // 我们不必去关注这些由语言类型决定的细节.
   CodeObjectCreateExpression newStatement = new
   CodeObjectCreateExpression ();
   // 指定我们要调用其构造器的对象.
   newStatement.CreateType = tr;
   // 变量将通过调用其构造器的方式初始化.
   Declaration.InitExpression = newStatement;
   return Declaration;
  }
  
  
  
    每一种.NET语言都有其特定的数据类型名称,所有这些数据类型都被映射到公共的.NET语言类型。例如,对于C#中称为int的数据类型,在VB.NET中是Integer,公共的.NET类型是System.Int32。CodeTypeReference对象直接使用.NET公共数据类型,以后由每种语言的CodeProvider将它转换成符合各自语言规范的类型名称。
  
    1.5 初始化数组
  
    初始化一个数组的代码类似下面这种形式:
  
  private void InitializeArray (string Name,
   params char[] Characters )
  {
   // 从参数中传入的字符数组获得一个CodeTypeReference 对象,
   // 以便在生成的代码中复制该数据类型.
   CodeTypeReference tr = new CodeTypeReference (Characters.GetType());
   // 声明一个匹配原始数组的数组
   CodeVariableDeclarationStatement Declaration =
   new CodeVariableDeclarationStatement (tr, Name);
   // CodePrimitiveExpression代表“基本”或值数据类型,
   // 例如char、int、double等等。
   // 我们将用这类基本数据类型构成的一个数组来
   // 初始化我们正在声明的数组。
   CodePrimitiveExpression[] cpe = new
   CodePrimitiveExpression[Characters.Length];
   // 循环遍历原始字符数组,
   // 为CodePrimitiveExpression类型的数组创建对象。
   for (int i = 0; i < Name.Length ; i++)
   {
   // 每一个CodePrimitiveExpression将有一个字符的语言
   // 中立的表示。
   cpe[i] = new CodePrimitiveExpression (Characters[i]);
   }
   // CodeArrayCreateExpression负责调用数组中数据类型的
   // 默认构造器。
   // 由于我们还传入了一个CodePrimitiveExpression的数组,
   // 所以不必指定数组的大小,且数组中的每一个元素都将有
   // 合适的初值。
   CodeArrayCreateExpression array = new
   CodeArrayCreateExpression(tr, cpe);
   // 指定:该CodeArrayCreateExpression将初始化数组变量声明。
   Declaration.InitExpression = array;
   return Declaration;
  }
  
  
  
    1.6 定义循环结构
  
    声明一个循环结构的代码类似下面这种形式:
  
  private CodeIterationStatement CreateLoop(string LoopControlVariableName)
  {
   // 声明一个新的变量,该变量将作为
   // 循环控制变量
   CodeVariableDeclarationStatement Declaration;
   // 声明一个管理所有循环逻辑的CodeIterationStatement
   CodeIterationStatement forloop = new CodeIterationStatement();
   // 为动态声明的变量指定数据类型的另一种方法:
   // 用typeof函数获得该数据类型的Type对象,不必
   // 用到该类数据的变量
   Declaration = new CodeVariableDeclarationStatement(typeof (int),
   LoopControlVariableName);
   // 指定一个简单的初始化表达式:
   // 将新变量设置为0
   Declaration.InitExpression = new CodeSnippetExpression ("0");
   // 这个新声明的变量将用来初始化循环
   forloop.InitStatement = Declaration;
   // CodeAssignStatement用来处理赋值语句。
   // 这里使用的构造器要求提供两个表达式,第一个位于
   // 赋值语句的左边,第二个位于赋值语句的右边。
   // 另一种办法是:调用默认的构造器,然后分别显式设置
   // 左、右两个表达式。
   CodeAssignStatement assignment = new CodeAssignStatement(
   new CodeVariableReferenceExpression(LoopControlVariableName),
   new CodeSnippetExpression (LoopControlVariableName + " + 1" ));
   // 在循环迭代中使用赋值语句。
   forloop.IncrementStatement = assignment;
   // 当循环控制变量超出数组中的字符个数时,
   // 循环结束
   forloop.TestExpression = new CodeSnippetExpression
   (LoopControlVariableName + " < Characters.Length");
   return forloop;
  }
  
  
  
    注意,这里我们用typeof函数直接获得循环控制变量的数据类型的Type对象,而不是通过声明一个CodeTypeReference对象的方式。这是CodeVariableDeclartionStatement的又一个构造器,实际上其构造器的总数多达7种。
  
    1.7 索引数组
  
    索引一个数组的代码类似下面这种形式:
  
  private CodeArrayIndexerExpression
   CreateArrayIndex(string ArrayName, string IndexValue )
  {
   // 新建一个CodeArrayIndexerExpression
   CodeArrayIndexerExpression index = new CodeArrayIndexerExpression ();
   // Indices属性是一个能够支持多维数组的集合。不过这里我们只需要
   // 一个简单的一维数组。
   index.Indices.Add ( new CodeVariableReferenceExpression (IndexValue));
   // TargetObject指定了要索引的数组的名称。
   index.TargetObject = new CodeSnippetExpression (ArrayName);
   return index;
  }
  
  
  
    CodeArrayIndexerExpression对象处理数组索引方式的种种差异。例如,在C#中数组以ArrayName[IndexValue]的方式索引;但在VB.NET中,数组以ArrayName(IndexValue)的方式索引。CodeArrayIndexerExpression允许我们忽略这种差异,将注意力集中到其他更重要的问题,例如要索引哪一个数组、要访问第几个数组元素。
  
    二、装配出树结构
  
    我们可以把前面定义的所有函数加入到一个类,通过构造器初始化,例如:
  
  public CodeDomProvider()
  {
   CurrentNameSpace = InitializeNameSpace("TestSpace");
   CodeTypeDeclaration ctd = CreateClass ("HelloWorld");
   // 把类加入到名称空间
   CurrentNameSpace.Types.Add (ctd);
   CodeEntryPointMethod mtd = CreateMethod();
   // 把方法加入到类
   ctd.Members.Add (mtd);
   CodeVariableDeclarationStatement VariableDeclaration =
   DeclareVariables (typeof (StringBuilder), "sbMessage");
   // 把变量声明加入到方法
   mtd.Statements.Add (VariableDeclaration);
   CodeVariableDeclarationStatement array = InitializeArray
   ("Characters", 'H', 'E', 'L', 'L', 'O', ' ',
   'W', 'O', 'R', 'L', 'D');
   // 把数组加入到方法
   mtd.Statements.Add (array);
   CodeIterationStatement loop = CreateLoop("intCharacterIndex");
   // 把循环加入到方法
   mtd.Statements.Add (loop);
   // 数组索引
   CodeArrayIndexerExpression index = CreateArrayIndex("Characters",
   "intCharacterIndex");
   // 加入一个语句,它将调用sbMessage对象的“Append”方法
   loop.Statements.Add (new CodeMethodInvokeExpression (
   new CodeSnippetExpression ("sbMessage"),"Append",
   index));
   // 循环结束后,输出所有字符追加到sbMessage对象
   // 后得到的结果
   mtd.Statements.Add (new CodeSnippetExpression
   ("Console.WriteLine (sbMessage.ToString())"));
  }
  
  
  
    构造器的运行结果是一个完整的CodeDom树结构。可以看到,至此为止我们的所有操作都独立于目标语言。最后生成的代码将以属性的形式导出。
  
    三、输出生成结果
  
    构造好CodeDom树之后,我们就可以较为方便地将代码以任意.NET语言的形式输出。每一种.NET语言都有相应的CodeProvider对象,CodeProvider对象的CreateGenerator方法能够返回一个实现了ICodeGenerator接口的对象。ICodeGenerator接口定义了用来生成代码的所有方法,而且允许我们定义一个用来简化属性输出的辅助方法。下面的辅助方法GenerateCode负责设置好合适的TextWriter以供输出代码,以字符串的形式返回结果文本。
  
  private string GenerateCode (ICodeGenerator CodeGenerator)
  {
   // CodeGeneratorOptions允许我们指定各种供代码生成器
   // 使用的格式化选项
   CodeGeneratorOptions cop = new CodeGeneratorOptions();
   // 指定格式:花括号的位置
   cop.BracingStyle = "C";
   // 指定格式:代码块的缩进方式
   cop.IndentString = " ";
   // GenerateCodeFromNamespace要求传入一个TextWriter以
   // 容纳即将生成的代码。这个TextWriter可以是一个StreamWriter、
   // 一个StringWriter或一个IndentedTextWriter。
   // StreamWriter可用来将代码输出到文件。
   // StringWriter可绑定到StringBuilder,后者可作为一个变量引用。
   // 在这里,我们把一个StringWriter绑定到StringBuilder sbCode。
   StringBuilder sbCode = new StringBuilder();
   StringWriter sw = new StringWriter(sbCode);
  
   // 生成代码!
   CodeGenerator.GenerateCodeFromNamespace(CurrentNameSpace, sw,cop);
   return sbCode.ToString();
  }
  
  
  
    有了这个辅助函数,要获取各种语言的代码就相当简单了:
  
  public string VBCode
  {
   get
   {
   VBCodeProvider provider = new VBCodeProvider ();
   ICodeGenerator codeGen = provider.CreateGenerator ();
   return GenerateCode (codeGen);
   }
  
  }
  
  public string JScriptCode
  {
   get
   {
   JScriptCodeProvider provider = new JScriptCodeProvider ();
   ICodeGenerator codeGen = provider.CreateGenerator ();
   return GenerateCode(codeGen);
   }
  
  }
  
  public string JSharpCode
  {
   get
   {
   VJSharpCodeProvider provider = new VJSharpCodeProvider ();
   ICodeGenerator codeGen = provider.CreateGenerator ();
   return GenerateCode (codeGen);
   }
  
  }
  
  public string CSharpCode
  {
   get
   {
   CSharpCodeProvider provider = new CSharpCodeProvider();
   ICodeGenerator codeGen = provider.CreateGenerator ();
   return GeneratorCode (codeGen);
   }
  
  }
  
  
  
    四、显示出生成的代码
  
    为输出代码,我们要用到一个简单的.aspx文件,它有四个标签,分别对应一种.NET语言:
  
  <table width="800" border="1">
   <tr>
   <th>VB.NET代码</th>
   </tr>
   <tr >
   <td>
   <asp:Label ID="vbCode" Runat="server" CssClass="code">
   </asp:Label>
   </td>
   </tr>
   <tr>
   <th>
   C#代码</th></tr>
   <tr>
   <td><asp:Label ID="csharpcode" Runat="server" CssClass="code">
   </asp:Label></td>
   </tr>
   <tr>
   <th>J#代码</th></tr>
   <tr >
   <td>
   <asp:Label ID="JSharpCode" Runat="server" CssClass="code">
   </asp:Label>
   </td>
   </tr>
   <tr>
   <th>JScript.NET代码</th>
   </tr>
   <tr>
   <td><asp:Label ID="JScriptCode" Runat="server" CssClass="code">
   </asp:Label></td>
   </tr>
  </table>
  
  
  
    在后台执行的代码中,我们实例化一个前面创建的CodeDomProvider类的实例,把它生成的代码赋值给.aspx页面的相应标签的Text属性。为了使Web页面中显示的代码整齐美观,有必要做一些简单的格式化,替换换行符号、空格等,如下所示:
  
  private string FormatCode (string CodeToFormat)
  {
   string FormattedCode = Regex.Replace (CodeToFormat, "\n", "<br>");
   FormattedCode = Regex.Replace (FormattedCode, " " , " ");
   FormattedCode = Regex.Replace (FormattedCode, ",", ", ");
   return FormattedCode;
  }
  
  
  
    下面把生成的代码显示到Web页面:
  
  private void Page_Load(object sender, System.EventArgs e)
  {
  
   HelloWorld.CodeDomProvider codegen = new HelloWorld.CodeDomProvider ();
   vbCode.Text = FormatCode (codegen.VBCode);
   csharpcode.Text = FormatCode (codegen.CSharpCode);
   JScriptCode.Text = FormatCode (codegen.JScriptCode);
   JSharpCode.Text = FormatCode (codegen.JSharpCode);
   Page.EnableViewState = false;
  }
  
  
  
    输出结果如下:
  
  VB.NET代码
  
  Imports System
  Imports System.Text
  
  Namespace HelloWorld
  
   Public Class Hello_World
  
   Public Shared Sub Main()
   Dim sbMessage As System.Text.StringBuilder = _
   New System.Text.StringBuilder
   Dim Characters() As Char = New Char() {_
   Microsoft.VisualBasic.ChrW(72), _
   Microsoft.VisualBasic.ChrW(69), _
   Microsoft.VisualBasic.ChrW(76), _
   Microsoft.VisualBasic.ChrW(76), _
   Microsoft.VisualBasic.ChrW(79), _
   Microsoft.VisualBasic.ChrW(32), _
   Microsoft.VisualBasic.ChrW(87), _
   Microsoft.VisualBasic.ChrW(79), _
   Microsoft.VisualBasic.ChrW(82), _
   Microsoft.VisualBasic.ChrW(76), _
   Microsoft.VisualBasic.ChrW(68)}
   Dim intCharacterIndex As Integer = 0
   Do While intCharacterIndex < Characters.Length
   sbMessage.Append(Characters(intCharacterIndex))
   intCharacterIndex = intCharacterIndex + 1
   Loop
   Console.WriteLine (sbMessage.ToString())
   End Sub
   End Class
  End Namespace
  
  C#代码
  
  namespace HelloWorld
  {
   using System;
   using System.Text;
  
   public class Hello_World
   {
   public static void Main()
   {
   System.Text.StringBuilder sbMessage = new
   System.Text.StringBuilder();
   char[] Characters = new char[] {
   'H',
   'E',
   'L',
   'L',
   'O',
   ' ',
   'W',
   'O',
   'R',
   'L',
   'D'};
   for (int intCharacterIndex = 0;
   intCharacterIndex < Characters.Length;
   intCharacterIndex = intCharacterIndex + 1)
   {
   sbMessage.Append(Characters[intCharacterIndex]);
   }
   Console.WriteLine (sbMessage.ToString());
   }
   }
  }
  
  J#代码
  
  package HelloWorld;
  import System.*;
  import System.Text.*;
  
  
  public class Hello_World
  {
   public static void main(String[] args)
   {
   System.Text.StringBuilder sbMessage = new
   System.Text.StringBuilder();
   char[] Characters = new char[]
   {
   'H',
   'E',
   'L',
   'L',
   'O',
   ' ',
   'W',
   'O',
   'R',
   'L',
   'D'}
   ;
   for (int intCharacterIndex = 0;
   intCharacterIndex < Characters.Length;
   intCharacterIndex = intCharacterIndex + 1)
   {
   sbMessage.Append(Characters[intCharacterIndex]);
   }
   Console.WriteLine (sbMessage.ToString());
   }
  }
  
  
  JScript.NET代码
  
  
  //@cc_on
  //@set @debug(off)
  
  import System;
  import System.Text;
  
  package HelloWorld
  {
  
   public class Hello_World
   {
  
   public static function Main()
   {
   var sbMessage : System.Text.StringBuilder =
   new System.Text.StringBuilder();
   var Characters : char[] =
   ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D'];
   for (var intCharacterIndex : int = 0;
   ; intCharacterIndex < Characters.Length;
   intCharacterIndex = intCharacterIndex + 1)
   {
   sbMessage.Append(Characters[intCharacterIndex]);
   }
   Console.WriteLine (sbMessage.ToString());
   }
   }
  }
  HelloWorld.Hello_World.Main();
  
  
  
    总结:CodeDom体现了.NET中语言的重要性不如框架的思想。本文示范了如何运用一些常用的CodeDom类,几乎每一个使用CodeDom技术的应用都要用到这些类。作为一种结构化的代码生成技术,CodeDom有着无限的潜能,唯一的约束恐怕在于人们的想象力。
posted @ 2005-04-13 11:16 HuYi's Blog 阅读(484) | 评论 (0)编辑 收藏

     一、发生的背景
    在开发新项目中使用了新的语言开发 C# 和新的技术方案 WEB Service,但是在新项目中,一些旧的模块需要继续使用,一般是采用 C 或 C++ 或 Delphi 编写的,如何利用旧模块对于开发人员来说,有三种可用方法供选择:第一、将 C 或 C++ 函数用 C# 彻底改写一遍,这样整个项目代码比较统一,维护也方便一些。但是尽管微软以及某些书籍说,C# 和 C++ 如何接近,但是改写起来还是很痛苦的事情,特别是 C++ 里的指针和内存操作;第二、将 C 或 C++ 函数封装成 COM,在 C# 中调用COM 比较方便,只是在封装时需要处理 C 或 C++ 类型和 COM 类型之间的转换,也有一些麻烦,另外COM 还需要注册,注册次数多了又可能导致混乱;第三、将 C 或 C++ 函数封装成动态链接库,封装的过程简单,工作量不大。因此我决定采用加载动态链接库的方法实现,于是产生了在 C# 中如何调用自定义的动态链接库问题,我在网上搜索相关主题,发现一篇调用系统 API 的文章,但是没有说明如何解决此问题,在 MSDN 上也没有相关详细说明。基于此,我决定自己从简单出发,逐步试验,看看能否达到自己的目标。
    (说明一点:我这里改写为什么很怕麻烦,我改写的代码是变长加密算法函数,代码有600多行,对算法本身不熟悉,算法中指针和内存操作太多,要想保证算法正确,最可行的方法就是少动代码,否则只要有一点点差错,就不能肯定算法与以前兼容)
  
  二、技术实现
    下面看看如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,这个不需要多说,大家参考下面宏定义即可:
  
  #define LIBEXPORT_API extern "C" __declspec(dllexport)
  第一步,我先从简单的调用出发,定义了一个简单的函数,该函数仅仅实现一个整数加法求和:
  
  LIBEXPORT_API int mySum(int a,int b){ return a+b;}
  C# 导入定义:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
      EntryPoint=" mySum ",
      CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
      public static extern int mySum (int a,int b);
  }
  在C#中调用测试:
  
  int iSum = RefComm.mySum(2,3);
  运行查看结果iSum为5,调用正确。第一步试验完成,说明在C#中能够调用自定义的动态链接库函数。
  
  第二步,我定义了字符串操作的函数(简单起见,还是采用前面的函数名),返回结果为字符串:
  
  LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,"%s",a); return a;}
  C# 导入定义:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Auto,
       CallingConvention=CallingConvention.StdCall)]
       public static extern string mySum (string a, string b);
  }
  在C#中调用测试:
  
  string strDest="";
  string strTmp= RefComm.mySum("12345", strDest);
  运行查看结果 strTmp 为"12345",但是strDest为空。我修改动态链接库实现,返回结果为串b:
  
  LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,"%s",a) return b;}
  修改 C# 导入定义,将串b修改为ref方式:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
       public static extern string mySum (string a, ref string b);
  }
  在C#中再调用测试:
  
  string strDest="";
  string strTmp= RefComm.mySum("12345", ref strDest);
    运行查看结果 strTmp 和 strDest 均不对,含不可见字符。再修改 C# 导入定义,将CharSet从Auto修改为Ansi:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
       public static extern string mySum (string a, string b);
  }
  在C#中再调用测试:
  
  string strDest="";
  string strTmp= RefComm. mySum("12345", ref strDest);
    运行查看结果 strTmp 为"12345",但是串 strDest 没有赋值。第二步实现函数返回串,但是在函数出口参数中没能进行输出。再次修改 C# 导入定义,将串b修改为引用(ref):
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
       public static extern string mySum (string a, ref string b);
  }
  运行时调用失败,不能继续执行。
  
  第三步,修改动态链接库实现,将b修改为双重指针:
  
  LIBEXPORT_API char *mySum(char *a,char **b){sprintf((*b),"%s",a); return *b;}
  C#导入定义:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
       public static extern string mySum (string a, ref string b);
  }
  在C#中调用测试:
  
  string strDest="";
  string strTmp= RefComm. mySum("12345", ref strDest);
    运行查看结果 strTmp 和 strDest 均为"12345",调用正确。第三步实现了函数出口参数正确输出结果。
  
  第四步,修改动态链接库实现,实现整数参数的输出:
  
  LIBEXPORT_API int mySum(int a,int b,int *c){ *c=a+b; return *c;}
  C#导入的定义:
  
  public class RefComm
  {
  [DllImport("LibEncrypt.dll",
       EntryPoint=" mySum ",
       CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
       public static extern int mySum (int a, int b,ref int c);
  }
  在C#中调用测试:
  
  int c=0;
  int iSum= RefComm. mySum(2,3, ref c);
  运行查看结果iSum 和c均为5,调用正确。
    经过以上几个步骤的试验,基本掌握了如何定义动态库函数以及如何在 C# 定义导入,有此基础,很快我实现了变长加密函数在 C# 中的调用,至此目标实现。
  
  三、结论
    在 C# 中调用 C++ 编写的动态链接库函数,如果需要出口参数输出,则需要使用指针,对于字符串,则需要使用双重指针,对于 C# 的导入定义,则需要使用引用(ref)定义。
    对于函数返回值,C# 导入定义和 C++ 动态库函数声明定义需要保持一致,否则会出现函数调用失败。定义导入时,一定注意 CharSet 和 CallingConvention 参数,否则导致调用失败或结果异常。运行时,动态链接库放在 C# 程序的目录下即可,我这里是一个 C# 的动态链接库,两个动态链接库就在同一个目录下运行。
  
  原文出处::http://windend.blogchina.com
posted @ 2005-04-13 11:13 HuYi's Blog 阅读(581) | 评论 (0)编辑 收藏

     摘要: 原文地址http://www.theserverside.com/articles/article.tss?l=SpringFramework You may have heard the buzz this summer around the Spring Framework. In this article, I'll try to explain what Spring sets out ...  阅读全文
posted @ 2005-04-04 10:35 HuYi's Blog 阅读(745) | 评论 (0)编辑 收藏

一、动态加载数据源
1、通过修改注册表加载数据源:
·用户数据源:HKEY_CURRENT_USERSOFTWAREODBCODBC.INI
·系统数据源:HKEY_LOCAL_MACHINESOFTWAREODBCODBC.INI
对于不同类型的数据源,注册表的修改也不同,但基本上要修改两个地方,一个是在ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在\ODBC.INIODBC Data Sources子键下建立一个新项以便告诉驱动程序管理器ODBC数据源的类型。
2、通过ODBC API加载:Windows系统子目录下的动态链接库Odbcinst.dll提供了一个可以动态增加、修改和删除数据源的函数SQLConfigDataSource,由于VC的默认库文件中不包含此函数,因此使用前需将Odbcinst.h文件包含在工程的头文件中,在工程的setting属性框Link页的Object/library module编辑框中增加Odbc32.lib,同时保证系统目录system32下有文件Odbccp32.dll
3、文件数据源的连接:除了ODBC管理器,还可以通过SQLDriverConnect来添加文件数据源。
二、ODBC  API编程
如果一个ODBC API函数执行成功,则返回SQL_SUCCESSSQL_SUCCESS_WITH_INFOSQL_SUCCESS指示可通过诊断记录获取有关操作的详细信息,SQL_SUCCESS_WITH_INFO指示应用程序执行结果带有警告信息,可通过诊断记录获取详细信息。如果函数调用失败,返回码为SQL_ERROR
一般,编写ODBC程序主要有一下几个步骤:
1、   分配环境句柄:声明一个SQLHENV的变量,调用函数SQLAllocHandle
设置环境属性:完成环境分配后,用函数SQLSetEnvAttr设置环境属性,注册ODBC版本号。
释放环境句柄:完成数据访问任务时,应调用SQLFreeHandle释放前面分配的环境。
2、       分配连接句柄:声明一个SQLHDBC类型的变量,调用SQLAllocHandle函数分配句柄。
设置连接属性:所有连接属性都可通过函数SQLSetConnectAttr设置,调用函数SQLGetConnectAttr可获取这些连接属性的当前设置值。
3、   连接数据源:对于不同的程序和用户接口,可以用不同的函数建立连接
SQLConnect:该函数只要提供数据源名称、用户ID和口令,就可以进行连接了。
SQLDriverConnect:该函数用一个连接字符串建立至数据源的连接,它可以让用户输入必要的连接信息,使用系统中还没定义的数据源。
SQLBrowseConnect:该函数支持以一种迭代的方式获取到数据源的连接,直到最后建立连接,它基于客户机/服务器体系结构,因此本地数据库不支持该函数。
4、   准备并执行SQL语句
A、  分配语句句柄:语句句柄是通过调用SQLAllocHandle函数分配的。
函数SQLGetStmrrAttrSQLSetStmrrAttr用来获取和设置一个语句句柄的选项,使用完,调用SQLFreeHandle释放该句柄。
B、  执行SQL语句
SQLExecDirect:该函数直接执行SQL语句,对于只执行一次的SQL语句来说,该函数是执行最快的方法。
SQLPrepareSQLExecute:对于需要多次执行的SQL语句来说,可先调用SQLPrepare准备SQL语句的执行,用SQLExecute执行准备好的语句。
C、  使用参数:使用参数可以使一条SQL语句多次执行,得到不同的结果。
函数SQLBindParameter负责为参数定义变量,将一段SQL语句中的一个参数标识符("?")捆绑在一起,实现参数值的传递。
5   获取记录集
A   绑定列:首先必须分配与记录集中字段相对应的变量,然后通过函数SQLBindCol将记录字段同程序变量绑定在一起,对于长记录字段,可以通过调用函数SQLGetData直接取回数据。
绑定字段可以根据自己的需要全部绑定,也可以绑定其中的某几个字段。
通过调用函数SQLBindCol将变量地址值赋为NULL,可以结束对一个记录字段的绑定,通过调用函数SQLFreeStmt,将其中选项设为SQL_UNBIND,或者直接释放句柄,都会结束所有记录字段的绑定。
BSQLFetch:该函数用于将记录集的下一行变成当前行,并把所有捆绑过的数据字段的数据拷贝到相应的缓冲区。
C 光标:应用程序获取数据是通过光标(Cursor)来实现的,在ODBC中,主要有3种类型的光标:单向光标、可滚动光标和块光标。
有些应用程序不支持可滚动光标和块光标,ODBC SDK提供了一个光标库(ODBCCR32.DLL),在应用程序中可通过设置连接属性(SQL_STTR_ODBC_CURSOR)激活光标库。
6  记录的添加、删除和更新:数据源数据更新可通过3种方式:通过SQLExecDirect函数使用相应的SQL语句;调用SQLSetPos函数实现记录集定义更新;调用SQLBulkOperations函数实现数据更新。
第一种方式适用于任何ODBC数据源,后两种方式有的数据源不支持,可调用SQLGetInfo确定数据源。
SQLBulkOperations:该函数操作基于当前行集,调用前,须先调用SQLFetchSQLFetchScroll获取。
函数调用后,块光标的位置变为未定义状况,因此,应该先调用函数SQLFetchScroll设定光标位置。
7、错误处理:每个ODBC API函数都能产生一系列反映操作信息的诊断记录,可以用SQLGetDiagField函数获取诊断记录中特定的域,另外,可以使用SQLGetDiagRec获取诊断记录中一些常用的域。
8、事务处理:事务提交有两种方式:自动提交模式和手动提交模式。应用程序可通过调用函数SQLSetConnectAttr设定连接属性SQL_ATTR_AUTOCOMMIT,自动提交模式是默认的连接属性设置,对于所有的ODBC驱动程序都能适应这种模式下,所有语句都是作为一个独立的事务进行处理的。
手动提交模式把一组SQL语句放入一个事务中,程序必须调用函数SQLEenTran明确地终止一个事务。若使用多个激活的事务,就必须建立多个连接,每一个连接包含一个事务。
9、断开数据连接并释放环境句柄:完成数据库操作后,可调用SQLDisconnect函数关闭同数据库的连接。
posted @ 2005-04-04 09:55 HuYi's Blog 阅读(821) | 评论 (0)编辑 收藏

1 Overview

1. What is AspectJ?

AspectJ(tm) is a simple and practical extension to the Java(tm) programming language that adds to Java aspect-oriented programming (AOP) capabilities. AOP allows developers to reap the benefits of modularity for concerns that cut across the natural units of modularity. In object-oriented programs like Java, the natural unit of modularity is the class. In AspectJ, aspects modularize concerns that affect more than one class.

You compile your program using the AspectJ compiler (perhaps using the supported development environments) and then run it, supplying a small (< 100K) runtime library.

The AspectJ technologies include a compiler (ajc), a debugger (ajdb), a documentation generator (ajdoc), a program structure browser (ajbrowser), and integration with Eclipse, Sun-ONE/Netbeans, GNU Emacs/XEmacs, JBuilder, and Ant.

2. What are the benefits of using AspectJ?

AspectJ can be used to improve the modularity of software systems.

Using ordinary Java, it can be difficult to modularize design concerns such as

  • system-wide error-handling

  • contract enforcement

  • distribution concerns

  • feature variations

  • context-sensitive behavior

  • persistence

  • testing

The code for these concerns tends to be spread out across the system. Because these concerns won't stay inside of any one module boundary, we say that they crosscut the system's modularity.

AspectJ adds constructs to Java that enable the modular implementation of crosscutting concerns. This ability is particularly valuable because crosscutting concerns tend to be both complex and poorly localized, making them hard to deal with.

3. Can AspectJ work with any Java program?

AspectJ has been designed as a compatible extension to Java. By compatible, we mean

upward compatible All legal Java programs are legal AspectJ programs.
platform compatible All legal AspectJ programs run on standard Java virtual machines.
tool compatible Existing tools can be extended to work with AspectJ.
programmer compatible Programming in AspectJ feels natural to Java programmers.

The AspectJ tools run on any Java 2 Platform compatible platform. The AspectJ compiler produces classes that run on any Java 1.1 (or later) compatible platform.

4. How is AspectJ licensed?

AspectJ 1.1 source code and documentation is available under the Common Public License 1.0.

The AspectJ 1.0 tools are open-source software available under the Mozilla Public License 1.1. That documentation is available under a separate license that precludes for-profit or commercial redistribution.

Most users only want to use AspectJ to build programs they distribute. There are no restrictions here. When you distribute your program, be sure to include all the runtime classes from the aspectjrt.jar for that version of AspectJ. When distributing only the runtime classes, you need not provide any notice that the program was compiled with AspectJ or includes binaries from the AspectJ project, except as necessary to preserve the warranty disclaimers in our license.

5. What is the AspectJ Project?

AspectJ is based on over ten years of research at Xerox Palo Alto Research Center as funded by Xerox, a U.S. Government grant (NISTATP), and a DARPA contract.

It has evolved through open-source releases to a strong user community and now operates as an open source project at http://eclipse.org/aspectj The AspectJ team works closely with the community to ensure AspectJ continues to evolve as an effective aspect-oriented programming language and tool set.

The latest release is 1.2 which can be downloaded from the AspectJ project page, including sources as described Q:How do I get and compile the source code for AspectJ?. Development is focused on supporting applications, improving quality and performance, enhancing integration with IDE's, and building the next generations of the language.

2 Quick Start

1. What Java versions does AspectJ require and support?

The AspectJ compiler produces programs for any released version of the Java platform (jdk1.1 and later). When running, your program classes must be able to reach classes in the small (< 100K) runtime library (aspectjrt.jar) from the distribution. The tools themselves require J2SE 1.3 or later to run, but the compiler can produce classes for any 1.1-compliant version of the Java platform.

2. How do I download and install AspectJ?

From AspectJ's web page , download the AspectJ distribution. The jar file is installed by executing

            java -jar jar file name
          

Do not try to extract the jar file contents and then attempt to execute java org.aspectj.tools.Main. (A NoClassDefFoundError exception will be thrown.) The AspectJ distribution is not designed to be installed this way. Use the java -jar form shown above.

To uninstall, remove the files the installer wrote in your file system. In most cases, you can delete the top-level install directory (and all contained files), after you remove any new or updated files you want to keep. On Windows, no registry settings were added or changed, so nothing needs to be undone. Do not install over prior versions, which might have different files. Delete the prior version first.

3. How should I start using AspectJ?

Many users adopt AspectJ incrementally, first using it to understand and validate their systems (relying on it only in development) and then using it to implement crosscutting concerns in production systems. AspectJ has been designed to make each step discrete and beneficial.

In order of increasing reliance, you may use AspectJ:

  • In the development process Use AspectJ to trace or log interesting information. You can do this by adding simple AspectJ code that performs logging or tracing. This kind of addition may be removed ("unplugged") for the final build since it does not implement a design requirement; the functionality of the system is unaffected by the aspect.

  • As an ancillary part of your system Use AspectJ to more completely and accurately test the system. Add sophisticated code that can check contracts, provide debugging support, or implement test strategies. Like pure development aspects, this code may also be unplugged from production builds. However, the same code can often be helpful in diagnosing failures in deployed production systems, so you may design the functionality to be deployed but disabled, and enable it when debugging.

  • As an essential part of your system Use AspectJ to modularize crosscutting concerns in your system by design. This uses AspectJ to implement logic integral to a system and is delivered in production builds.

一:总揽

1什么是AspectJ
AspectJ是JAVA程序语言的简单并且实用的一种扩充。它增加了JAVA aspect-oriented progamming(AOP)即面向方面编程的工能。它能使程序员从模块单元的横切关系上获知益。像面向对象编语言JAVA,模块之间的一种自然的关联是建立在类。也就是class上的。在AspectJ中,这种关联会涉及到一多个模块。

你可以用aspectJ进行你的程序开发(不包括运行环境),一个运行库大约不到100k.

AspectJ 技术包括一个编译器ajc,一个调式器ajdb,一个通用文档ajdoc,一个程序结构浏览器ajbrowser,还有一个支持这个架构的运行环境,Eclipse,SUN-ONE/Netbeans,GUNEmacs/XEmacs,JBuilder,和Ant.

2使用AspectJ的好处


AspectJ可以用于改进软件模块的性能。
使用JAVA语言,在 处理系统异常(system-wide error-handling),契约式编程(contract enforcement),分布式(distribution concerns),可变性(feature variations),上下文相关行为(context-sensitive behavior),持久化(persistence),测试(testing)在这些方面,模块之间的关联很难设计。在这些方面,代码不能分布在整个系统中,而不能封装在一个类中,这便是系统中的一种横切关系。

AspectJ提供的构架使模块能够实现横切关系。实现系统中的横切关系是很有价值并且必要的,因为系统中这些分散于各个模块的代码,使我们的设计变得复杂,局部混乱,并且乱于管理与维护。

3AspectJ能应该于任何系统中吗?


AspectJ可以和任何JAVA程序兼容。这种兼容是包括以下几个方面

向上兼容:所有标准的JAVA程序,都是标准的Aspect程序。
平台兼容:所有标准的Aspect程序,都是运行于JAVA虚拟机上。
工具兼容:现有工具可以被延用到AspectJ中使用。
程序兼容:在AspectJ中编写的程序,对于JAVA程序来说,感觉也会很自然,很熟悉。


二:快速入门

1.AspectJ要求并支持的JAVA版本?

AspectJ可以为JAVA平台1.1以后的版本发布代码。运行时,你要使自己程序的类可以访问到AspectJ运行类aspectj.jar(这个包小于100k).

2.怎样下载AspectJ的安装文件?

从AspectJ的网页上,下载AspectJ的正式版。java-jar×××解压后,安装。


不要尝试分解jar文件的内容,或者分离工具包中的类。(NoClassDefFoundError exception 没有创建的类 这个异常会被抛出。 )不能这样安装.还是要按着解压后安装的方式,进行安装。

卸载时,要删除掉已经写入的文件。在大多情况下,再你把要保存的新的或刚刚更新过的文件移开时,你可以删除最上层的安装目录。在windows下,不用注册,所以不用做什么其它事情就可以正常使用了。不要覆盖安装当前版本,因为可能出现新的文件。安装新版本前,请删除原来的文件。

3怎样开始使用AspectJ

许多用户常常会先用AspectJ来了解验证自己的系统,然后再去用AspectJ去执行横切。AspectJ设计本意使设计阶段连续不间段的获利。

在以下几个方面你可以自信的使用AspectJ:

1在系统开发过程中:使用AspectJ来描述或记录特殊信息。你可以通过简单的AspectJ代码来实现日志记录和信息描述。如果系统不需要执行这些功能的时候,也可以把这个新增的功能最终的系统中拿掉,而原来的系统不会受任何的影响。

2系统的辅助部分:使用AspectJ来完善并且精确的测试你的系统。AspectJ增加了精典的代码来检查系统规范,支持检查系统漏洞,或者执行系统测试。就像AOP的本质一样,这些代码一样可以被从最终的系统中删除。这些代码在检查现有系统的问题的时候还是很有帮助的,所以我们可以把这功能设计出来,但是不在系统运行时,有实际的作用,而只在测试时产生作用。

3系统核心部分:使用AspectJ来对模块进行横切设计。这种使用AspectJ来对系统执行的逻辑规范将被包括在系统中。

posted @ 2005-04-01 15:58 HuYi's Blog 阅读(993) | 评论 (0)编辑 收藏