clr via c#重要知识点和本人的一些感想Word文档格式.docx
《clr via c#重要知识点和本人的一些感想Word文档格式.docx》由会员分享,可在线阅读,更多相关《clr via c#重要知识点和本人的一些感想Word文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
3.CLR在进行程序集的搜索时,具有这样一个规则:
搜索附属程序集时,不会关注程序集的版本号,只使用语言文化信息;
弱命名程序集总是私有部署的,所以当CLR在应用程序的基目录或者子目录中搜索程序集的文件时,只会使用程序集的名称(添加一个.dll或者.exe扩展名)。
4.“对一个文件进行签名”时,确切的含义是:
生成一个强命名程序集时,程序集的FileDef清单元数据表列出了构成程序集的所有文件,每次将一个文件的名称添加到清单中,文件的内容都会进行哈希处理,得到的哈希值会和文件名一道存储在FileDef表中。
要想覆盖默认哈希算法,可使用AL.exe的/algid开关,也可在程序集的某个源代码文件中,在assembly这一级上应用System.Reflection.AssemblyAlgorithmIdAttribute这个定制attribute。
默认使用的是SHA-1算法,它对几乎所有应用程序来说都足够了。
生成了包含清单的PE文件后,会对PE文件的完整内容(任何AuthenticodeSignature、程序集强名称数据以及PE头校验和除外)进行哈希处理,如图3-1所示。
此时使用的哈希算法始终是SHA-1,而且不可更改。
这个哈希值使用发布者的私钥进行签名,最终得到的RSA数字签名会存储到PE文件的一个保留区域(进行哈希处理时,会忽略这个区域)。
PE文件的CLR头会进行更新,反映出数字签名在文件中的嵌入位置。
5.GacUtil工具:
将一个强名称程序集安装到全局程序集缓存中。
注意:
默认情况下,GAC只能由WindowsAdministrators组的成员操作。
如果执行GACUtil.exe的用户不是这两个组的成员,GACUtil.exe将无法安装或卸载程序集。
GACUtil.exe的/i开关方便开发人员在测试时使用。
然而,用GACUtil.exe在生产环境中部署程序集时,建议在安装或卸载程序集时,除了指定GACUtil.exe的/i或/u开关,还要使用/r开关。
/r开关将程序集与Windows的安装与卸载引擎集成到一起。
简单地说,它告诉系统哪个应用程序需要程序集,并将应用程序与程序集绑定到一起。
如果将强命名程序集打包到一个.cab文件中,或者以其他方式进行压缩,程序集的文件首先必须解压成临时文件,然后才能使用GACUtil.exe将程序集文件安装到GAC中。
安装好程序集的文件之后,临时文件可以删除
6.token命名规则:
0x01typeRef;
0x02typedef;
0x023assemblyRef;
0x026Filedef;
0x027FileRef;
7.强命名程序集能防篡改。
用一个私钥对程序集进行签名,可保证程序集是由对应公钥的持有者生成的。
程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,并将哈希值与PE文件中嵌入的RSA数字签名进行比较(在用公钥解除了对它的签名之后)。
如果两个值完全一致,表明文件的内容未被篡改,可保证你拿到的公钥与发布者的私钥是对应的。
除此之外,系统还会对程序集的其他文件的内容进行哈希处理,并将哈希值与清单文件的FileDef表中存储的哈希值进行比较。
任何一个哈希值不匹配,表明程序集至少有一个文件被篡改,
程序集将无法安装到GAC。
将一个强命名的程序集安装到GAC时,系统会执行一次检查,核实含有清单的那个文件没有被篡改。
这个检查只在安装时执行一次。
除此之外,为了增强性能,如果程序集被完全信任,并加载到一个完全信任的AppDomain,CLR不检查强命名的程序集是否被篡改。
相反,从非GAC的一个目录加载强命名程序集时,CLR会校验程序集的清单文件,核实文件的内容未被篡改,造成该文件每次加载都会带来额外的性能开销。
8.延迟签名。
使用sn工具可生成公钥/私钥对,这个工具在生成密钥时,会调用Windows提
供的CryptoAPI。
当你准备好对自己的强命名程序集进行打包时,必须使用受到严密保护的私钥对它进行签名。
然而,在开发和测试程序集时,访问这些受到严密保护的私钥可能有些费事儿。
有鉴于此,.NETFramework提供了对延迟签名(delayedsigning)的支持,这个技术有时也称为部分签名(partialsigning)。
延迟签名允许你只用公司的公钥来生成一个程序集,暂时不用私钥。
由于使用了公钥,引用了你的程序集的那些程序集会在它们的AssemblyRef元数据表的记录项中嵌入正确的公钥值。
另外,它还使程序集能正确存储到GAC的内部结构中。
当然,不用公司的私钥对文件进行签名,便无法实现防篡改保护。
这是由于无法对程序集的文件进行哈希处理,无法在文件中嵌入一个数字签名。
然而,失去这种保护不是一个大问题,因为只是在开发阶段才延迟签名。
打包和部署程序集时,肯定是会签名的。
为了实现延迟签名,你需要获取存储在一个文件中的公钥值,将文件名传给用于生成程序集的任何实用程序。
(如本章前面所述,可以使用SN.exe的-p开关从包含一个公钥/私钥对的文件中提取一个公钥值。
)另外,还必须让工具知道你想延迟对程序集的签名,暂时不提供一个私钥。
如果使用C#编译器,可以指定/delaysign编译器开关。
如果使用VisualStudio,需打开项目属性对话框,然后在“签名”选项卡中勾选“仅延迟签名”。
如果使用AL.exe,可以指定/delay[sign]命令行开关。
编译器或AL.exe一旦检测到要对一个程序集进行延迟签名,就会生成程序集的AssemblyDef清单记录项,其中将包含程序集的公钥。
公钥使程序集能正确地放到GAC中。
另外,这也不妨碍引用了这个程序集的其他程序集的正确生成。
在进行引用的程序集的AssemblyRef元数据表记录项中,会包含(被引用程序集)的正确公钥。
创建程序集时,会在生成的PE文件为RSA数字签名预留空间(实用程序能根据公钥大小来判断需要预留多大的空间)。
要注意的是,文件内容不会在这个时候进行哈希处理。
目前生成的程序集没有一个有效地签名,默认的将它安装到GAC会失败。
我们需要使用一种机制使系统在安装程序集是跳过这个验证。
实现这个机制的工具是SN工具,通过制定-vr开关,clr在加载程序集的文件时,会跳过对其哈希值的检查。
在内部,SN的-Vr开关会将程序集的身份添加到以下注册表子项中:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification
结束一个程序集的开发和测试之后,需要正式对其进行签名,以便打包和部署它。
为了对程序集进行签名,要再次使用SN.exe实用程序,但这一次换用-R开关,并指定包含了私钥的文件的名称。
-R开关指示SN.exe对文件内容进行哈希处理,用私钥对其进行签名,并将RSA数字签名嵌入文件中之前预留的空间。
经过这一步之后,就可以部署完全签好名的程序集。
在开发和测试机器上,不要忘记使用SN.exe的-Vu或-Vx命令行开关来重新启用对这个程序集的验证。
下面总结了使用延迟签名技术开发程序集的步骤:
1.开发程序集期间,获取只包含你的公司的公钥的一个文件,使用/keyfile和/delaysign编译器开关编译程序集:
csc/keyfile:
MyCompany.PublicKey/delaysignMyAssembly.cs
2.生成程序集之后,执行以下命令,使CLR暂时信任程序集的内容,不对它进行哈希处理,也不对哈希值进行比较。
这使程序集能顺利安装到GAC(如果有必要的话)。
现在,可以生成引用了这个程序集的其他程序集,并可随意测试程序集。
注意,在每台开发用的机器上,以下命令行都只需执行一次,不必在每次生成程序集时都重复这一步:
SN.exe-VrMyAssembly.dll
3.准备好打开和部署程序集时,请获取你的公司的私钥,然后执行以下命令。
如果愿意,可以将这个新版本安装到GAC中。
但是,只有在完成了步骤4之后,才能把它安装到GAC中。
SN.exe-RMyAssembly.dllMyCompany.PrivateKey
4.为了在实际环境中测试,请执行以下命令行,重新启用对这个程序集的验证:
SN–VuMyAssembly.dll
提示:
打包一个程序集前,只要你希望对它执行其他任何操作,延迟签名都非常有用。
例如,可能想要对自己的程序集运行一个混淆器(obfuscator)程序。
程序集在完全签名后,便不能再对它运行混淆器,否则哈希值就不正确了。
所以,要想混淆一个程序集文件,或者进行其他形式的“生成后”(post-build)操作,就应该使用延迟签名,执行所有必要的“生成后”操作,再用-R或-Rc开关运行SN.exe,最终完成对程序集的签名。
安全知识:
大企业会把它们的密钥对放到硬件设备(比如智能卡)中。
为了确保这些密钥的安全性,密钥值绝对不能持久存储在一个磁盘文件中。
“加密服务提供程序”(CryptographicServiceProvider,CSP)提供了对这些密钥的位置进行抽象的容器。
以Microsoft使用的CSP为例,一旦访问它提供的容器,就会自动从一个硬件设备中获取私钥。
9.强命名程序集除了部署到GAC,或者以私有方式部署之外,还可部署到只有少数应用程序才知道的某个目录。
例如,假定要生成3个应用程序,这三个应用程序应用程序都共享一个强命名程序集。
安装时可以创建3个目录,每个目录都放置一个应用程序。
除此之外,再创建第4个目录,专门用于存储想要共享的程序集。
将每个应用程序安装到它的目录时,都同时安装一个XML配置文件,在配置文件的codeBase元素指
出共享程序集的路径。
这样在运行时,CLR就知道跑去那里查找共享程序集。
但要注意,这个技术很少使用,也不太推荐使用,因为没有任何一个应用程序能决定在什么时候卸载程序集的文件。
配置文件的codeBase元素实际标记了一个URL。
这个URL可引用用户机器上的任何目录,也可引用一个Web地址。
如果引用的是Web地址,CLR会自动下载文件,并把它存储到用户的下载缓存中(%UserProfile%\LocalSettings\ApplicationData\Assembly下的一个子目录)。
将来进行引用时,CLR会将已下载的文件的时间戳与指定URL处的文件的时间戳进行对比。
如果URL处的文件具有较新的时间戳,CLR会下载新版本文件并加载它。
否则,CLR会加载现有的文件,不再重复下载(从而增强性能)。
10..关于运行时程序集的查找问题。
在运行时,可以向System.AppDomain的AssemblyResolve,ReflectionOnlyAssemblyResolve和TypeResolve事件注
册你写的回调方法。
在回调方法中,可执行一些能够解决绑定问题的代码,使应用程序在不抛出异常的前提下继续运行。
强命名程序集的搜索方式:
CLR会检查GAC或者由codeBase元素指定的URL。
只有在未指定codeBase元素的前提下,CLR才会在应用程序的私有路径中检查强命名程序集。
11.类型从一个程序集移动到另一个程序集。
CLR提供了将一个类型(包括类、结构、枚举、接口或委托)从一个程序集移动到另一个程序集的功能。
例如,在.NET3.5中,System.TimeZoneInfo类是在System.Core.dll程序集中定义的。
但在.NET4.0中,Microsoft将这个类移动到了MSCorLib.dll程序集。
将一个类型从一个程序集移动到另一个程序集,这一般情况下会造成应用程序的“中断”。
但是,CLR提供了一个名为System.Runtime.CompilerServices.TypeForwardedToAttribute的attribute,可将它应用于原始程序集(比如System.Core.dll)。
要向这个attribute的构造器传递一个System.Type类型的参数,它指出应用程序要使用的新类型(现在是在MSCorLib.dll中定义)。
CLR的binder(绑定器)会利用这个信息。
由于TypeForwardedToAttribute的构造器获取的是一个Type,所以包含这个attribute的程序集要依赖于类型的定义程序集。
要使用这个功能,还要将System.Runtime.CompilerServices.TypeForwardedFromAttribute这个attribute应用于新程序集中的类型,并向该attribute的构造器传递一个字符串来指出对类型进行了定义的新程序集的全名。
这个attribute通常用于工具、实用程序和序列化。
由于TypeForwardedFromAttribute的构造器获取的是一个String,所以包含这个attribute的程序集不依赖于类型的定义程序集。
12.管理员高级管理配置。
这里指的是如何控制CLR搜索程序集的方式,使用probling元素的privatePath可以制定CLR搜索程序集的路径。
下面是一个程序集配置文件:
程序集名称.exe.config:
?
xmlversion="
1.0"
AuxFiles;
bin\subdir"
dependentAssembly>
assemblyIdentityname="
JeffTypes"
publicKeyToken="
32ab4ba45e0a69a1"
culture="
neutral"
/>
bindingRedirect
oldVersion="
1.0.0.0"
newVersion="
2.0.0.0"
codeBaseversion="
href="
http:
//www.W/>
/dependentAssembly>
TypeLib"
1f2e74e897abbcfe"
3.0.0.0-3.5.0.0"
4.0.0.0"
publisherPolicyapply="
no"
解释1:
第一个dependentAssembly,assemblyIdentity和bindingRedirect元素查找由控制着公钥标记32ab4ba45e0a69a1的那个组织发布的、语言文化为中性的JeffTypes程序集的1.0.0.0版本时,改为定位同一个程序集的2.0.0.0版本。
codeBase元素查找由控制着公钥标记32ab4ba45e0a69a1的组织发布的、语言文化为中性的JeffTypes程序集的2.0.0.0版本时,尝试在以下URL处发现它:
www.W
codeBase元素也能用于弱命名程序集。
在这种情况下,程序集的版本号会被忽略,而且根本就不应该在XMLcodeBase元素中写这个版本号。
此外,codeBase所定义的URL必须引用应用程序基目录下的一个子目录。
解释2:
第二个dependentAssembly,assemblyIdentity和bindingRedirect元素查找由控制着公钥标记1f2e74e897abbcfe的那个组织发布的、语言文化为中性的TypeLib程序集的3.0.0.0到3.5.0.0版本时(包括3.0.0.0和3.5.0.0在内),改为定位同一个程序集的4.0.0.0版本。
解释3:
publisherPolicy元素如果生成TypeLib程序集的组织部署了一个发布者策略文件,CLR应该忽略该文件。
这个功能很强大
13.发布者策略控制文件。
通过上面那个配置文件可以控制CLR加载程序集的方式,但是这样的工作让管理员或是普通用户来做,存在难度较大,而且容易出错的可能。
我们需要一种更加可靠地方式来让CLR自动应用这个机制。
使用发布者控制策略文件就可做到这一点,下面是一个用于JeffTypes.dll的发布者策略文件(只有发布者才能创建,对自己发布的新程序集负责),名为JeffTypes.config:
//www.W
上面的元素是发布者唯一能使用的元素,不可使用probing和publisherPolicy元素,这两元素应是用户来控制的。
这个配置文件指示CLR一旦发现对JeffTypes程序集的1.0.0.0版本的引用,就自动加载2.0.0.0版本。
现在,作为发布者,可创建包含这个发布者策略配置文件的一个程序集。
为了创建发布者策略程序集,要像下面这样运行AL.exe:
AL.exe/out:
Policy.1.0.JeffTypes.dll
/version:
1.0.0.0
/keyfile:
MyCompany.snk
/linkresource:
JeffTypes.config
下面是对AL.exe的命令行开关的解释:
l/out这个开关指示AL.exe创建一个新的PE文件,本例就是Policy.1.0.JeffTypes.dll,其中除了一清单之外什么都没有。
程序集的名称很重要。
名称的第一部分(Policy)告诉CLR该程序集包含发布者策略信息。
第二部分和第三部分(1.0)告诉CLR这个发布者策略程序集适用于major和minor版本为1.0的任何版本的JeffTypes程序集。
发布者策略只能和程序集的major和minor版本关联;
不能和build或revision号关联。
名称的第四部分(JeffTypes)指出与这个发布者策略对应的程序集的名称。
名称的第五部分(dll)是现在要生成的发布者策略程序集文件的扩展名。
/version这个开关标识发布者策略程序集的版本;
这个版本号与JeffTypes程序集本身没有任何关系。
看得出来,发布者策略程序集本身也有一套版本机制。
例如,今天,发布者可以创建一个发布者策略,将JeffTypes的1.0.0.0版本重定向到版本2.0.0.0。
将来,发布者可以把JeffTypes的版本1.0.0.0重定向到版本2.5.0.0。
CLR根据/version开关指定的版本号来选择最新版本的发布者策略程序集。
/keyfile这个开关指示AL.exe使用发布者的“公钥/私钥对”对发布者策略程序集进行签名。
这个密钥对还必须匹配于所有版本的JeffTypes程序集的密钥对。
毕竟,只有这样,CLR才知道JeffTypes程序集和发布者策略文件是由同一个发布者创建的。
/linkresource这个开关告诉AL.exe将XML配置文件作为一个资源链接(而非嵌入)到程序集。
最后的程序集是由两个文件构成的(现在要生成的Policy.1.0.JeffTypes.dll和刚才已经创建好的XML配置文件JeffTypes.config),两个文件都必须随同新版本的JeffTypes程序集打包并部署到用户机器上。
顺便说一句,不能使用AL.exe的/embedresource开关将XML配置文件嵌入程序集文件,从而获得一个单文件的程序集。
这是因为CLR要求XML文件包含在它自己的一个单独的文件中。
一旦生成这个发布者策略程序集,就可随同新的JeffTypes.dll程序集文件打包并部署到用户机器上。
发布者策略程序集必须安装到GAC中。
虽然JeffTypes程序集也能安装到GAC中,但并不是必须的——它可以部署到应用程序的基目录,或者部署到由一个codeBaseURL标识的其他目录中。
14.CLR控制字段内存布局的方式。
为了能提高性能,clr能按照它认为比较好的任何方式来布局内存,你可以指示CLR是严格按照自己指定的顺序排列,还是采取CLR自己认为合适的方式重新排列。
为了指示CLR应该怎么做,需要为自己定义的类或结构应用System.Runtime.InteropServices.StructLayoutAttribute这个attribute。
可以向这个attribute的构造器传递LayoutKind.Auto,让CLR自动排列字段;
也可以传递LayoutKind.Sequential,让CLR保持你的字段布局;
也可以传递LayoutKind.Explicit,利用偏移量在内存中显式排列字段。
如果不为自己定义的类型显式指定St