原文:http://www.blogcn.com/User8/flier_lu/index.html?id=3765092
与传统 Windows 中目录存储定位,以及 COM 中注册表定位等方式不同,CLR 中使用了几级组件定位策略,以试图彻底解决 DLL Hell 的问题。其中 GAC 是 CLR 中代码共享的最重要的中心存储库,所有的强签名 (Strong Named) 配件 (Assembly) 都应该通过 GAC 来进行共享和版本管理。但遗憾的是 BCL 中并没有直接提供对 GAC 的控制和管理功能,而是通过一个名为 gacutil.exe 的命令行工具进行管理。麻烦的是这个界面并不友好的工具是不随 .NET Framework 发布版本分发的,必须在安装程序中自带安装。
好在 CLR 提供了一个虽然没有写入正式文档,但通过 KB317540 半公开的 Fusion API 用以控制 GAC 等代码存储机制。
这些接口和函数的定义文件在 .NET FX 1.1 中可在 SDK 的 Include/Fusion.h(.idl) 文件中找到,而在 FX 2.0 beta 中则只有 Include/Fusion.h,大概是因为 Fusion 以及完全合并到 mscorwks.dll 里面去了的原因吧。
这套 API 主要分为三部分功能:Assembly 列表及信息的获取;Assembly 的安装、删除和信息查询;Assembly 安装引用信息的获取与设置。
Assembly 列表及信息的获取功能是我们获得详细信息的最重要接口。
首先,枚举 ASM_CACHE_FLAGS 中定义了缓存所在的位置:
typedef enum
|
|
ZAP 是 ngen 生成本地代码的缓存;GAC 是全局强名字组件的缓冲;DOWNLOAD 则是 Internet 下载组件的缓存。可以使用 GetCachePath 函数获得这三种缓存的物理目录。
public class Fusion
|
|
对每种缓存,都可以通过 CreateAssemblyCache 函数获取一个 Assembly 枚举器 IAssemblyEnum 接口实例,然后通过 IAssemblyEnum::GetNextAssembly 函数遍历所有容器中的 Assembly。
public class Fusion
|
|
对每一个 Assembly 都可以通过 IAssemblyEnum::GetNextAssembly 函数获得表现其名称信息的 IAssemblyName,通过此接口可以进一步获取 Assembly 的细节信息,如名称、版本号、Culture、Public Key 等等。可以通过一个 AssemblyName 类将之封装起来。
public class AssemblyName
|
|
IAssemblyName 接口本身提供了一些常用信息的直接的获取方法,如 Assembly 名字和版本号信息:
public class AssemblyName
|
|
不过更加常用的是通过 GetDisplayName 方法获取类似 "System.Configuration.IgnoreSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 这样格式的显式名称:
public class AssemblyName
|
|
标志 ASM_DISPLAY_FLAGS 定义了希望获得由哪些部分组成的显式名称
[Flags] public enum ASM_DISPLAY_FLAGS
|
|
一般来说使用由名称、版本号、Culture和Public Key Token 组成的显式名称。
如果希望获取或设置更细致的信息,则可以使用 GetProperty/SetProperty 方法。
public class Fusion
|
|
枚举值 ASM_NAME 用于指定要获取哪个数据,可以通过一个 Data 类和 PropertyCollection 类进一步包装直接对数据进行的访问。
public class AssemblyName
|
|
这样一来使用时只需要通过 Properties 属性的索引进行访问即可,如
asmName.Properties[AssemblyProperty.PublicKeyToken].ToString() asmName.Properties[AssemblyProperty.Custom].ToString() |
|
除了不同缓存的 Assembly 列表获取以外,对 Assembly 的安装、卸载和信息查询功能也经常用到。可以通过 CreateAssemblyCache 函数获取一个 IAssemblyCache 接口实例,进而对缓存进行操作。
public class Fusion
|
|
在安装 Assembly 时,需要通过 pszManifestFilePath 参数指定 Assembly 的 manifest 文件所在路径。对大多数 Assembly 来说,只有一个独立的 dll 包括了所有的信息和数据;但某些情况下,一个 Assembly 可能被编译为一个包含 manifest 信息的主 Module 辅以多个延迟获取的网络分发的辅助 Module。例如不同语言的资源文件 Module 可能独立存在,直到需要的时候才从网络上获取。
此外还可用通过 dwFlags 参数指定安装的策略,如 IASSEMBLYCACHE_INSTALL_FLAG_REFRESH 表示如果安装文件较新则替换缓存中原有文件;而 IASSEMBLYCACHE_INSTALL_FLAG_FORCE_REFRESH 标记则不考虑版本号强制进行替换,对应于 gacutil 的 /f 参数。
最后还可以使用 FUSION_INSTALL_REFERENCE 结构指定 GAC 中此 Assembly 所依赖的外部信息,但这种方式降低了 GAC 的重用性,不被推荐也很少用到,这儿暂且忽略。
public class Fusion
|
|
卸载时则只需要给出 Assembly 的完整名称即可,注意是包括版本号等信息的名称。如果安装时指定了外部依赖信息,则必须给出同样信息。IAssemblyCache::UninstallAssembly 方法会返回一个状态信息 ASM_UNINSTALL_DISPOSITION,表示卸载成功与否。
public class Fusion
|
|
此外还可以通过 IAssemblyCache::QueryAssemblyInfo 方法查询某个 Assembly 是否被安装、缓存中的文件名、以及缓存中文件的大小等等信息。
public class Fusion
|
|
如果指定 QUERYASMINFO_FLAG_VALIDATE 参数则 fusion 会对缓存中文件进行验证;如果指定 QUERYASMINFO_FLAG_GETSIZE 则通过 uliAssemblySizeInKB 返回 Assembly 的大小。
最后还可以通过 CreateInstallReferenceEnum 函数和 IInstallReferenceEnum 接口,浏览安装 Assembly 时给出的外部依赖信息;或者通过 IAssemblyCache::CreateAssemblyCacheItem 方法和 IAssemblyCacheItem 接口,控制缓存项目内容。不过这些高级功能一般也用不上,这儿就不再罗嗦了。
在 .NET Framework 2.0 beta 中,Fusion API 做了不大的调整:对缓存类型增加了 ASM_CACHE_ROOT 标记,表示在对某个缓存类型进行操作时的影响范围;还增加了 PEKIND 枚举类型提供对多平台支持;Assembly 属性 ASM_NAME 也做了相应扩展,增加了对文件版本号、平台架构等等信息的支持:
public enum PEKIND
|
|
一个新增的有趣特性是支持对两个 Assembly 的比较,能够检查两个 Assembly 的相似性。
typedef enum _tagAssemblyComparisonResult { ACR_Unknown = 0, ACR_EquivalentFullMatch = ACR_Unknown + 1, ACR_EquivalentWeakNamed = ACR_EquivalentFullMatch + 1, ACR_EquivalentFXUnified = ACR_EquivalentWeakNamed + 1, ACR_EquivalentUnified = ACR_EquivalentFXUnified + 1, ACR_NonEquivalentVersion = ACR_EquivalentUnified + 1, ACR_NonEquivalent = ACR_NonEquivalentVersion + 1, ACR_EquivalentPartialMatch = ACR_NonEquivalent + 1, ACR_EquivalentPartialWeakNamed = ACR_EquivalentPartialMatch + 1, ACR_EquivalentPartialUnified = ACR_EquivalentPartialWeakNamed + 1, ACR_EquivalentPartialFXUnified = ACR_EquivalentPartialUnified + 1, ACR_NonEquivalentPartialVersion = ACR_EquivalentPartialFXUnified + 1 } AssemblyComparisonResult;
STDAPI CompareAssemblyIdentity(LPCWSTR pwzAssemblyIdentity1, BOOL fUnified1, LPCWSTR pwzAssemblyIdentity2, BOOL fUnified2, BOOL *pfEquivalent, AssemblyComparisonResult *pResult);
|
|
完整的实现例子短期内可以从这里下载:
http://flier.5i4k.net/GacUtilW.rar
btw: 感谢 Junfeng Zhang 在 Fusion 方面的强力支持 
相关文章:
-
2021-08-18
-
2021-04-09
-
2021-12-18
-
2021-08-18
-
2022-12-23
-
2022-02-07
-
2021-09-24
-
2021-05-09