C#-程序集

C#-程序集

                                      

程序集

===============================================================================================
     ----------------------------------------------------------------------1 什么是程序集? ----------------------------------------------------------------------
 DLL的版本问题,程序不知道到底该使用哪个DLL版本,从而产生中断。
 DLL-Hell----〉与DLL相关的问题
 .NET对DLL-Hell及其所有问题的答案是使用程序集
程序集-是自我描述的安装单元,由一个或多个文件组成,一个程序集可以是一个包括元数据的DLL或EXE,也可以由多个文件组成。
 另一优点是可以使私有或共享的。
 私有程序集和共享程序集有很大的区别
 版本冲突问题必须在开发阶段解决
 程序集的特性: 1 程序集是自我描述的;
      2 版本的互相依赖性在程序集的清单中进行了记录
      3 程序集可以进行并行加载 ,同一个DLL的不同版本可以在同一个系统上同时使用
      4 应用程序使用应用程序域来确保其独立性(Application Domain)
      5 安装非 常简单,只要复制一个程序集中的所有文件,一个xcopy命令就够了--无干涉部署(No-touch Deployment)

 应用程序集和应用程序域

      -----------------------  |     ----------------------------
       进程一        进程二 
 ____________  |  ____________
 应用程序域A
 应用程序域B  |
      ......
 ____________  |  ____________
    -------------------------     ----------------------------
进程之间有边界,应用程序域存在于某个进程中,之间也有边界,处在不同应用程序域中的应用程序不能互相访问
应用程序域可以互相包含

------------------------------------------
======================
进程〉应用程序域〉程序集〉对象
======================
------------------------------------------
应用程序域之间可以通信,或使用代理
AppDomain类用于创建和中断应用程序域,加载和卸载程序集和类,枚举域中的程序集和线程
例如: 1--创建一个控制台应用程序AssemblyA
 2--第二个应用程序DomainTest加载AssemblyA.exe程序集
/////////////////////////////////////////////////////////////////////////////////////////
 using System;
 namespace Wrox.ProCSharp.Assemblies.AppDomains
 {
  class Class1
  {
   public Class1(int  val1,int val2)
   {//构造函数,具有两个参数,以了解如何创建AppDomain类的实例
    Console.WriteLine("Constructor with the values {0},{1}"+
      "in domain {2} called",val1,val2,
      AppDomain.CurrentDomain.FriendlyName);
   } 
   [STAThread]
   static void Main(string[] args)
   {//在Main中,从而知道什么时候调用Console.WriteLine
    Console.WriteLine("Main in domain {0} called",
      AppDomain.CurrentDomain.FriendlyName);
   }
  }
 }
//////// /////////////////////////////////////////////////////////////////////////////////
 using System;
 namespace Wrox.ProCShap.Assemblies.AppDomains
 {
  class Test
  {
   [STAThread]
   static void Main(string[] args)
   {
    AppDomain currentDomain = AppDomain.CurrentDomain;
    Console.WriteLine(currentDomain.FriendlyName);
    //使用AppDomain类的FriendlyName属性显示当前域的名称

    ////FLAG////
    AppDomain.secondDomain = AppDomain.CreateDomain("New AppDomain");
    //创建一个新的应用程序域New AppDomain

    secondDomain.ExecuteAssembly("AssemblyA.exe");
    //把程序集AssemblyA加载到新域中,通过调用ExecuteAssembly来调用Main()方法
   }
  }
 }

在启动DomainTest.exe前,必须把程序集AssemblyA.exe复制到DomainTest.exe所在的目录下,这样程序才能找到
这个程序集,不能添加AssemblyA.exe程序集的引用,因为在VS.NET中,只能给以DLL格式存储的程序集添加引用,不支持
EXE格式,但是EXE格式的程序集可以在命令行上执行,如果找不到这个程序集就会抛出System.IO.FileNotFoundExection异常
结果:Main in domain New AppDomain called

在上面第二个程序表明的FLAG处可以改为:再创建一个实例,用于替代Main()方法
/////////////////////////////////////////////////////////////////////////////////////////
 AppDomain secondDomain = AppDomain.CreateDomain("New AppDomain");

 secondDomain.CreateInstance("AssemblyA" ,"Wrox.ProCShap.Assemblies.AppDomains.Class1",
    true,System.Reflection.BindingFlags.CreateInstance,null,new object[]{7,3}
    null,null,null);
///////////////////////////////////////////////////////////////////////////////////////// 
//1-程序集名;2-应实例化的类;3-true表示不区分大小写;4-邦定标志枚举值,指定应调用的构造函数;
得到的输出为:Constructor with the values 7,3 in domain New AppDomain called


 在运行期间主应用程序域会自动创建,
 ASP.NET为每个运行在WEB服务器上的Web应用程序创建一个应用程序域,
 Internet Explorer创建运行托管控件的应用程序域,
 卸载应用程序域只能通过中断应用程序域来进行


     ----------------------------------------------------------------------2 程序集的结构 ----------------------------------------------------------------------
 程序集由=描述它的元数据(程序集元数据)+描述导出类型和方法的类型元数据+MSIL代码+资源
存在于一个文件中或分布在多个文件中
例如: 1 程序集由一个文件组成:Component.dll
 2 Component.dll=描述它的元数据+描述导出类型和方法的类型元数据+MSIL代码,
  资源不在其中
  这个程序集使用了一个图:Pictrue.jpeg,该图没有嵌入在dll中,而是在程序集的元数据中引用
  程序集的原数据还引用了一个模块:Util.netmodule,该模块只包含一个类的类型元数据和MSIL代码
  不包含程序集的元数据,所以这个模块没有版本信息,也不能单独安装
  这3个文件构成了程序集,这个程序集是一个安装单元,还可以在另外一个文件中放置程序集清单
   Component.dll  Util.netmodule
  ________________________ ____________
Picture.jpeg<------- 程序集元数据  -------〉 类型元数据
__________    -------〉 IL代码
资源  类型元数据   
  IL代码

 程序集的清单
元数据的一部分,描述了程序集和引用它所需的所有信息,并列出,了所有的依赖关系
清单=标识(名称,版本,文化,公钥)+属于该程序集的一个文件列表+
 引用程序集的列表+一组许可请求--运行的许可证+
 导出的类型(当它们在一个模块中定义,该模块在程序集中引用,否则不属于清单)

---------------------------------------------
======================
 命名空间,程序集和组件
1 命名空间完全独立于程序集
2 一个程序集中可以有不同的命名空间
3 一个命名空间也可以分布在多个程序集中
4 命名空间只是类名的一种扩展,它属于类名的范畴
======================
---------------------------------------------

======================
 私有程序集和共享程序集
在使用共享程序集时,程序集必须是唯一的,名称唯一(强名),该名称的一部分是一个强制的版本号,如第三方控件
======================
======================
 查看程序集
命令行工具:ILDASM

FILE-->OPEN-->打开程序集
======================
======================
 构建程序集
1-可以创建模块:csc /target:module hello.cs 模块是一个没有程序集特性的DLL,可以添加到程序集中
 创建了hello.netmodule
2-生成一个程序集B.DLL ,它包含模块A.netmodule: csc /target:library /addmodule:A.netmodule /out:B.dll
3-模块的作用1是可以更快的启动程序集,因为并不是所有类都在一个文件中
  模块只在需要时加载
 2是是否需要使用多种编程语言来创建一个程序集:一个模块用VB.net,一个模块用C#,这两个模块都包含在一个程序集中
4-使用VS创建程序集
VS2003不支持直接创建模块
创建一个项目时,系统自动生成源文件AssemblyInfo.cs,在该文件中可以使用一般的源代码编辑器配置程序集的属性
[assembly]和[module]是程序集的全局属性

System.Reflection命名空间中的类:
AssemblyCompany    ---指定公司名
AssemblyConfiguration   --指定建立信息,例如零售或调试信息
AssemblyCopyright/Assembly Trademark --包含版权和商标信息
AssemblyDefaultAlias   --如果程序集名称不容易理解,如动态创建程序集名称时的GUID,就可以使用该属性,指定一个别名
AssemblyDescription   --描述程序集或产品,如果查看可执行文件的属性,这个值就会显示为Comments
AssemblyProduct    --指定了属于该程序集的产品名称
AssemblyInfomationalVersion   --在引用程序集时,这个属性不用于版本检查,仅用于版本信息,非常适用于指定使用多个程序集的应用程序的版本,打开可执行程序的属性,这个值就显示为Product Version
AssemblyTitle    --是程序集的描述性名称,可以包括空格,查看属性时显示为Description

System.Runtime.CompilerServices命名空间中的类:
AssemblyCulture     --程序集的文化背景,如en-US
AssemblyDelaySign/AssemblyKeyFile/AssemblyKeyName --用于创建共享程序集的强名
AssemblyVersion     --指定程序集的版本号,版本问题在共享程序集中具有非常重要的地位
======================

======================
 跨语言支持
.NET 使用通用类型系统Common Type System-CTS-定义了如何在.net中定义值类型和引用类型,以及这些类型的内存布局
但CTS没有确保在任何语言中定义的类型都可以用于其它语言。
这应是公共语言规范Common Language Specification-CLS 的任务。
CLS定义了.net语言必须支持的最低要求
======================

 CTS=common type system=通用类型系统
 CLS=common language specification=公共语言规范
======================
例如:
**********************************************************************************
Visual C++编写的基类HelloMCPP/   继承于HelloMCPP的类HelloVB/  继承于HelloVB的类HelloCSharp
 HelloMCPP  HelloVB   HelloCSharp
 
 +Hello()   +Hello()   +Hello()
 +Hello2()   +Add()   +Add()
 +Add()      +Main()
**********************************************************************************
--使用VS2003创建一个VC的类-class library(.net)
1>HelloMCPP
 //HelloMCPP.h
 #pragma once
 #i nclude <stdio.h>
 using namespace System;
 namespace Wrox
 {namespace ProCSharp
 {namespace Assemblies
 {namespace CrossLanguage
 {public _gc class HelloMCPP //_gc 标记类HelloMCPP,使类成为一个托管类
 {public:
  virtual void Hello()
  {Console::WriteLine(S"Hello,ManagedC++");}//S作为字符串的前缀,托管的字符串就会写入程序集,并用ldstr推入堆栈
  
  virtual void Hello2()
]  {printf("Hello,calling native code \n");}

  int Add(int val1,int val2)
  {return val1+val2;}
 };
 }}}}
**********************************************************************************
--使用VS2003创建一个VB.net的类-class library
--打开项目属性,在Root Namespace 中将项目的根命名空间改为Wrox.ProCSharp.Assemblies.CrossLanguage
,这样就改变了类的命名空间
--添加对HellpMCPP的引用:Project-->Add Reference
给项目添加引用就是将引用的程序集复制到VB.net项目的输出目录上(/bin),然后对原引用程序集的改变就是独立的
2>HelloVB
 public class HelloVB
  Inherits HelloMCPP

  public Overrides Sub Hello()
   MyBase.Hello() 'MyBase关键字代表基类,此处调用基类的方法
   Console.WriteLine("Hello,VB.NET")
  End Sub

  public Shadows Function Add(ByVal val1 as integer,_
    ByVal val2 as integer) as integer
   'Shadows关键字用来隐藏基类的方法Add(),因为在基类中Add()不是虚拟(virtual)的
   '不能被重写
   return val1+val2
  End Function
 End class
**********************************************************************************
--创建一个C#控制台应用程序,添加对HelloVB和HelloMCPP的引用
3>HelloCSharp
 using System;
 namespace Wrox.ProCSharp.Assemblies.CrossLanguage
 {
  public class HelloCSharp:HelloVB
  {
   public HelloCSharp()
   {}

   public override void Hello()
   {
    base.Hello();
    Console.WriteLine("Hello,C#");
   }

   public new int Add(int val1,int val2)
   {
    return val1+val2;
   }

   [STAThread]

   public static void Main()
   {
    HelloCSharp hello new HelloCSharp();
    hello.Hello();
   }
  }
 }
    
**********************************************************************************
最后控制台应用程序的输出如下:
 Hello,Managed C++
 Hello,VB.NET
 Hello,C#
*************************
因为所有的.net语言都生成MSIL代码,所有的语言都使用.NET FRAMEWORK中的类,所以在性能上是没有区别的
但仍有一些小的差别,首先,由于语言的不同,某些语言支持的数据类型其它语言不支持
其次,生成的MSIL代码仍有差别
在默认配置下,VB.NET上的执行比较安全,C#上的执行比较快,C#也更灵活
*************************
在基类中定义的方法在都可以在派生类中调用,如果方法的参数是System.UInt32数据类型,就不能在VB.NET中使用它
,因为VB.NET不支持无符号数据
无符号的数据类型与CLS不兼容,.net的语言不必支持这种数据类型
**********************************************************************************
.NET的语言
 .NET consumer工具 
  只使用.NET FRAMEWORK中的类,不能创建用于其它语言的.NET类;
  可以使用任何与CLS兼容的类
 .NET extends工具
  可以满足客户的要求,可以继承任何与CLS兼容的.NET类;
  定义了可以由客户使用的新CLS兼容类
  C++,VB.NET,C#都是.NET extender工具,使用这些语言可以创建CLS兼容类

**********************************************************************************
CLSCompliant属性
 利用它可以把程序集标记为与CLS兼容,这样可以确保这个程序集中的类能用于所有的.NET consumer工具;
 在公共方法或受保护的方法中使用与CLS不兼容的数据类型时,编译器会给出警告;
 在私有方法中使用什么样的数据类型则不重要,因为在类的外部使用其它语言时,根本就不能访问私有方法;
 当在公共方法和受保护的方法中使用与CLS不兼容的类型时,为了让编译器发出警告,可以设置程序集中的属性
CLSCompliant,把这个属性添加到AssemblyInfo.cs中:
 [assembly:System.CLSCompliant(true)]
这样,在程序集中定义的所有类型和公共方法就都是兼容的,当参数的数据类型是不兼容的UINT时,编译器就会发出如下
警告:error CS3001:Argument type uint is not CLS-compliant
把程序集标记为兼容时,仍可以定义不兼容的方法,如果要重写某些方法,使其参数是兼容和不兼容的数据类型,就必须把
类中的不兼容的方法的CLSCompliant属性设置为false
CLSCompliant 属性可以用到类型,方法,属性,字段和事件上:
 [CLSCompliant(false)]
 void Method(uint i)
 {//......
**********************************************************************************
CLS规则
程序集和与CLS兼容的要求:
 方法原形中的所有类型都必须与CLS兼容;
 数组元素的元素类型必须与CLS兼容。数组的第一个元素的下标必须是0;
 CLS兼容类必须继承与CLS兼容类,当然,System.Object 是与CLS兼容的;
 在CLS兼容类中,方法名士不区分大小写的,两个方法不能仅根据其名称中字母的大小写来区分;
 枚举的类型必须是Int16,Int32,Int64,其它类型的枚举都是不兼容的;
上述要求只适用于公共成员和受保护的成员,私有方法则无需考虑这些要求,它们可以使用不兼容的类型,
而程序集仍然是兼容的
还应该遵循更一般的命名约定
C#  VB.NET 更一般
int integer Int32
long long Int64
float single Single
在利用CLS规范和规则进行编译时,很容易创建出可以用于多种语言的组件,不需要使用所有的.NET FrameWork语言来测试该组件
**********************************************************************************
全局程序集缓存 Global Assembly Cache
 可全局使用的程序集的缓存
 大多数共享程序集都安装在这个缓存中,其中也安装了一些私有程序集
 如果私有程序集使用本机图像生成器编译为本机代码,编译好的本机代码也会存储在这个缓存中
**********************************************************************************
本机图像生成器 native image generator Ngen.exe
 可以在安装期间把IL代码编译为本机代码,这样程序启动就比较快,因为不再需要在运行时进行编译
 Ngen工具在本机图像缓存中安装本机图像,本机图像缓存是全局程序集缓存的一部分

**********************************************************************************
全局程序集缓存查看器
 全局程序缓存可以使用shfusion.dll来显示,它是一个Windows外壳扩展程序,可以查看和处理缓存的内容
Windows外壳扩展程序是一个与Windows资源管理器集成的COM DLL
 用户启动资源管理器,进入<windir>/assembly目录即可
 可以查看全局程序集的名称,类型,版本,文化和公钥标记,通过查看全局程序集的类型可以确定程序集是否
是使用本机图像生成器安装的
在该目录下,有GAC和NativeImages_<runtime version>目录,分别是共享程序集的目录和编译为本机代码的程序集

**********************************************************************************
全局程序集缓存工具 gacutil.exe
 全局程序集缓存查看器可以查看和删除程序集,但不能在脚本代码中使用该工具,例如创建安装程序。
 gacutil.exe 可以使用命令行安装,卸载和显示程序集
 选项: gacutil /l  --显示程序集缓存中的所有程序集
  gacutil /i mydll --把共享程序集mydll安装到程序集缓存上
  gacutil /u mydll --卸载程序集mydll
  gacutil /ungen mydll --从本机图像缓存中卸载程序集
**********************************************************************************
创建共享程序集
 程序集可以由一个应用程序使用,在默认下部共享程序集,在使用私有程序集时,不需要考虑共享时需要考虑的任何要求
 共享程序集名:必须是全局唯一的,1要保护该名称,2要使得其他人不能使用这个名称创建程序集
  COM使用全局唯一标识符GUID只解决了第一个问题,第二个问题仍然没有解决,每个人
都可以盗用这个GUID,用相同的标识符创建不同的对象
  这两个问题使用.NET程序集的强名都可以解决
  强名由以下项目组成:
    程序集本身的名称
    版本号
    公钥,保证强名是独一无二的,并且保证引用的程序集不能被另一个源替代
    文化
 共享程序集必须有一个强名,来唯一的标识该程序集
 每个程序集不能有新的公钥,但可以在公司中有这样一个公钥,这样该密钥就唯一的标识了公司的程序集
 但是 ,这个密钥不能用作信任密钥,程序集可以利用Authenticode签名来建立信任关系,
 Authenticode中的密钥可以与强名中使用的密钥不同

**********************************************************************************
公钥的加密
 对称加密:使用同一个密钥进行加密和解密
 公钥/私钥加密:使用一个公钥加密,使用对应的私钥解密/使用一个私钥加密,使用对应的公钥解密
   成对创建公钥和私钥,公钥可以任何人使用,甚至可以放在web站点上,但私钥必须安全的加锁
 Sarah-->one email -->Julian,除了Julian外的人都不能看
 使用Julian的公钥加密,Julian打开该email,并使用他秘密存储的私钥解密
 但还有一个问题,Julian不能确保email是Sarah发来的,任何人都可以使用Julian的公钥加密发送email给他
 解决办法是党Sarah发送email给Julian时,使用Julian的公钥加密邮件之前他添加了自己的签名,再使用自己的
 私钥加密该签名,然后使用Julian的公钥加密email,这个签名可以使用Sarah的公钥来解密,而Julian可以
访问Sarah的公钥,在解密了签名后,Julian就可以确定是Sarah发送了email

**********************************************************************************
将公钥/私钥应用于程序集
 创建共享组件,必须使用公钥/私钥对
 编译器把公钥写入程序集清单,创建属于该程序集的所有文件的散列表--用私钥标记这个散列表
 私钥不存储在程序集中,确保没有人可以修改这个程序集,签名可以使用公钥来验证
 在开发过程中,客户程序集必须引用 共享程序集。编译器把引用程序集的公钥写入客户程序集的清单中
 要减少存储量,就不应把公钥写入客户程序集的清单,而应写入公钥标记,公钥标记是公钥散列表中的最后8位字节,且是唯一的
 在运行期间加载共享程序集时(如果客户程序集是使用本机图像生成器安装的,则应在安装期间加载),
 共享程序集的散列表可以使用存储在客户程序集中的公钥来验证,除了私钥的主人外其他人都不能修改共享程序集
 例如:
 销售商A创建了一个组件Math,在客户机上引用该组件,黑客的组件就无法替代它,只有私钥的主人才能用
新版本来替换原来的共享组件,保证了其完整性

**********************************************************************************
创建共享程序集
 例子
 1 建立一个Visual C# Class Library 项目 SharedDemo,把命名空间改为Wrox.ProCSharp.Assemblies.Sharing
     类名改为SimpleShared
 using System;
 using System.Collection.Specialized;
 using System.IO;
 
 namespace Wrox.ProCSharp.Assemblies.Sharing
 {
  public class SharedDemo
  {
   private StringCollection quotes;//类的构造函数将文件的所有行都读到其中
      //文件名作为参数传递到构造函数
   private Random random;
   
   public SharedDemo(string filename)
   {
    quotes = new StringCollection();
    Stream stream =File.OpenFile(filename);
    StreamReader streamReader = new StreamReader(stream);
    string quote;
    
    while((quote=streamReader.ReadLine())!=null)
    {
     quotes.Add(quote);
    }
    streamReader.Close();
    stream.Close();
    random = new Random();
   }
   public string GetQuoteOfTheDay()
   //返回这个集合的一个随机字符串
   {
    int index = random.Next(1,quotes.Count);
    retrun quotes[index];
   }
  }
 }
*************************
* a>创建强名称 *--程序集中有了公钥
*************************
  要共享这个组件,需要一个强名称,要创建这个名称可以使用强名称工具sn:
  sn -k mykey.snk
 强名称工具生成和编写一个公钥/私钥对,并把该密钥对写到文件中,此处的文件是mykey.snk,现在可以
在向导生成的文件Assemblyinfo.cs中设置属性AssemblyKeyFile,该属性可以设置为密钥文件的绝对路径,也可以
设置为密钥文件的相对路径%ProjectDirectory\obj\<configuration>目录,所以http://www.cnblogs.com/mykey.snk引用项目目录中的
一个密钥,在开始建立一个项目时,该密钥安装到Crypto Service Provider(CSP)中,如果该密钥已经安装到CSP中,就可以
使用AssemblyKeyName属性
 下面是对Assemblyinfo.cs的修改
 [assembly:AssemblyDelaySign(false)]
 [assembly:AssemblyKeyFile("http://www.cnblogs.com/mykey.snk")]
 [assembly:AssemblyKeyName("")]
在重新建立该文件后,使用ildasm查看该程序集,则该程序集的清单中应有一个公钥

*************************
* b>安装共享程序集 *
*************************
 使用全局程序集缓存工具gacutil及其/I选项把它安装到全局程序集缓存中:
 gacutil /i SharedDemo.dll
 可以使用全局程序集缓存查看器检查共享程序集的版本,看看它是否安装成功

*************************
* c>使用共享程序集 *
*************************
 创建一个C#控制台应用程序Client,不是把新项目添加到原来的解决方案中,而是创建一个新的解决方案
这样在重新建立客户时,就不会重新建立该共享程序集了。
 以引用私有程序集方式引用程序集SimpleShared.dll,使用菜单Project|Add Reference
 有了共享程序集,引用属性CopyLocal就可以设置为false,这样,共享程序集就不会复制到输出文件的目录下
而会从全局程序集缓存中加载
下面是客户机应用程序的代码:
 using System;
 namespace Wrox.ProCSharp.Assemblies.Sharing
 {
  class Client
  {
   [STAThread]
   static void Main(string[] args)
   {
    SharedDemo quotes = new SharedDemo(@"C:\ProCSharp\Assemblies\Quotes.txt");
    for(int i=0;i<3;i++)
    {
     Console.WriteLine(quotes.GetQuoteOfTheDay());
     Console.WriteLine();
    }
   }
  }
 }
**********************************************************************************
公钥的标记也可以使用强名称工具sn在共享程序集中查看:sn -T会显示程序集中的公钥标记
sn -Tp显示标记和公钥
**********************************************************************************
程序集的延迟签名
 公司的私钥应安全存储,大多数公司不允许所有的开发人员访问私钥,只有几个有安全权限的人才能访问
这就是程序集的签名可以以后(例如发布前)添加的原因
 全局程序集属性AssemblyDelaySign设置为true时,签名就不会存储在程序集中,但保留了足够的空间,以便
以后添加。
 但是不使用密钥就不能测试程序集,在全局程序集缓存中安装它
 但可以使用临时密钥进行测试,以后再用真正的密钥代替这个临时密钥
 程序集的延时签名需要执行以下步骤:
 1>使用sn创建一个公钥/私钥对,生成文件mykey.snk,包含公钥和私.snsn -k mykey.snk
 2>提取公钥,使之可以用于开发人员。选项-p提取密钥文件的公钥,文件mypublickey.snk仅包含公钥
  sn -p mykey.snk mypublickey.snk
 公司中所有开发人员都可以使用这个密钥文件mypublickey.snk
 在文件AssemblyInfo.cs中设置AssemblyDelaySign和AssemblyKeyFile属性
 [assembly:AssemblyDelaySign(true)]
 [assembly:AssemblyKeyFile("http://www.cnblogs.com/mypublickey.snk")]
 3>关闭签名的验证功能,因为程序集没有包含签名
 sn -Vr ShareDemo.dll
 4>在发布之前,程序集可以用sn工具重新签名
 -R选项用于对以前已签名或延迟签名的程序集进行重新签名
 sn -R MyAssembly.dll mykey.snk
注意:签名的验证功能只能在开发过程中关闭,不经过验证是不能发布程序集的,因为这个程序集可能被怀有恶意的程序集代替


                                                  摘自:http://www.5blogs.com/kianhsu/6058/archives/2006/12689.html

posted @ 2008-03-26 09:40  魏向峰  阅读(994)  评论(0编辑  收藏  举报