find dirname -type f -exec egrep "from[ ]{0,}portfolio.*? " {} \;
			
			
			
		 
		
	
		
			
			
			
				    .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有着无限的潜能,唯一的约束恐怕在于人们的想象力。 
			
			
			
		 
		
	
		
			
			
			
				     一、发生的背景 
    在开发新项目中使用了新的语言开发 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 
			
			
			
		 
		
	
		
			
			
			
				     摘要: 原文地址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 ...  
阅读全文
			 
			
			
		 
		
	
		
			
			
			
				一、动态加载数据源
1、通过修改注册表加载数据源:
·用户数据源:HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI
·系统数据源:HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI
对于不同类型的数据源,注册表的修改也不同,但基本上要修改两个地方,一个是在ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在\ODBC.INI\ODBC 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_SUCCESS或SQL_SUCCESS_WITH_INFO,SQL_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函数分配的。
函数SQLGetStmrrAttr和SQLSetStmrrAttr用来获取和设置一个语句句柄的选项,使用完,调用SQLFreeHandle释放该句柄。
B、  执行SQL语句
SQLExecDirect:该函数直接执行SQL语句,对于只执行一次的SQL语句来说,该函数是执行最快的方法。
SQLPrepare和SQLExecute:对于需要多次执行的SQL语句来说,可先调用SQLPrepare准备SQL语句的执行,用SQLExecute执行准备好的语句。
C、  使用参数:使用参数可以使一条SQL语句多次执行,得到不同的结果。
函数SQLBindParameter负责为参数定义变量,将一段SQL语句中的一个参数标识符("?")捆绑在一起,实现参数值的传递。
5、   获取记录集
A、   绑定列:首先必须分配与记录集中字段相对应的变量,然后通过函数SQLBindCol将记录字段同程序变量绑定在一起,对于长记录字段,可以通过调用函数SQLGetData直接取回数据。
绑定字段可以根据自己的需要全部绑定,也可以绑定其中的某几个字段。
通过调用函数SQLBindCol将变量地址值赋为NULL,可以结束对一个记录字段的绑定,通过调用函数SQLFreeStmt,将其中选项设为SQL_UNBIND,或者直接释放句柄,都会结束所有记录字段的绑定。
B、SQLFetch:该函数用于将记录集的下一行变成当前行,并把所有捆绑过的数据字段的数据拷贝到相应的缓冲区。
C、 光标:应用程序获取数据是通过光标(Cursor)来实现的,在ODBC中,主要有3种类型的光标:单向光标、可滚动光标和块光标。
有些应用程序不支持可滚动光标和块光标,ODBC SDK提供了一个光标库(ODBCCR32.DLL),在应用程序中可通过设置连接属性(SQL_STTR_ODBC_CURSOR)激活光标库。
6、  记录的添加、删除和更新:数据源数据更新可通过3种方式:通过SQLExecDirect函数使用相应的SQL语句;调用SQLSetPos函数实现记录集定义更新;调用SQLBulkOperations函数实现数据更新。
第一种方式适用于任何ODBC数据源,后两种方式有的数据源不支持,可调用SQLGetInfo确定数据源。
SQLBulkOperations:该函数操作基于当前行集,调用前,须先调用SQLFetch或SQLFetchScroll获取。
函数调用后,块光标的位置变为未定义状况,因此,应该先调用函数SQLFetchScroll设定光标位置。
7、错误处理:每个ODBC API函数都能产生一系列反映操作信息的诊断记录,可以用SQLGetDiagField函数获取诊断记录中特定的域,另外,可以使用SQLGetDiagRec获取诊断记录中一些常用的域。
8、事务处理:事务提交有两种方式:自动提交模式和手动提交模式。应用程序可通过调用函数SQLSetConnectAttr设定连接属性SQL_ATTR_AUTOCOMMIT,自动提交模式是默认的连接属性设置,对于所有的ODBC驱动程序都能适应这种模式下,所有语句都是作为一个独立的事务进行处理的。
手动提交模式把一组SQL语句放入一个事务中,程序必须调用函数SQLEenTran明确地终止一个事务。若使用多个激活的事务,就必须建立多个连接,每一个连接包含一个事务。
9、断开数据连接并释放环境句柄:完成数据库操作后,可调用SQLDisconnect函数关闭同数据库的连接。