DLL是Microsoft的共享库实现。共享库允许将公共代码捆绑到包装器DLL中,并由系统上的任何应用程序软件使用,而无需将多个副本加载到内存中。一个简单的例子可能是GUI文本编辑器,它被许多程序广泛使用。通过将此代码放在DLL中,系统上的所有应用程序都可以使用它而无需使用更多内存。这与静态库形成对比,静态库功能相似,但将代码直接复制到应用程序中。在这种情况下,每个应用程序都会增加它使用的所有库的大小,对于现代程序来说这可能非常大。
当计算机上的DLL版本与创建程序时使用的版本不同时,会出现问题。 DLL没有用于向后兼容的内置机制,甚至对DLL的微小更改使其内部结构与以前的版本不同,尝试使用它们通常会导致应用程序崩溃。静态库避免了这个问题,因为用于构建应用程序的版本包含在其中,因此即使系统上的其他位置存在较新的版本,这也不会影响应用程序。
版本不兼容的一个关键原因是DLL文件的结构。该文件包含DLL中包含的各个方法(过程,例程等)的目录以及它们获取和返回的数据类型。即使对DLL代码进行微小的更改也可能导致重新排列此目录,在这种情况下,调用特定方法的应用程序认为它是目录中的第4项可能最终会调用完全不同且不兼容的例程,这会通常会导致应用程序崩溃。
DLL经常遇到一些问题,特别是在系统上安装和卸载了许多应用程序之后。困难包括DLL版本之间的冲突,难以获得所需的DLL,以及具有许多不必要的DLL副本。
DLL不兼容的原因是:
内存限制,以及16位版本的Windows中缺少进程内存空间的分离;
缺乏针对DLL的强制标准版本控制,命名和文件系统位置模式;
缺乏用于软件安装和删除的强制标准方法(包管理);
缺乏对DLL应用程序二进制接口管理和安全措施的集中权威支持,允许发布具有相同文件名和内部版本号的不兼容DLL;
过度简化的管理工具,防止用户和管理员识别已更改或有问题的DLL;
开发人员破坏共享模块中函数的向后兼容性;
Microsoft发布对操作系统运行时组件的带外更新;
早期版本的Windows无法运行同一个库的并行冲突版本;
依赖于当前目录或%PATH%环境变量,它们随时间和系统而变化,以查找相关的DLL(而不是从显式配置的目录中加载它们);
开发人员将示例应用程序中的ClassID重用于其应用程序的COM接口,而不是生成自己的新GUID。
DLL Hell在Windows NT之前版本的Microsoft操作系统上是一种非常普遍的现象,主要原因是16位操作系统没有将进程限制到自己的内存空间,因此不允许它们加载自己的版本的他们兼容的共享模块。在覆盖现有系统DLL之前,应用程序安装程序应该是好公民并验证DLL版本信息。 Microsoft和其他第三方工具供应商提供了简化应用程序部署的标准工具(始终涉及运送相关的操作系统DLL)。在授予使用Microsoft徽标之前,Microsoft甚至要求应用程序供应商使用标准安装程序并使其安装程序经过认证,以使其正常工作。良好的公民安装方法并没有缓解这个问题,因为互联网普及的增加为获得不合格的应用程序提供了更多的机会。
多年来,各种形式的DLL地狱已经被解决或减轻。
应用程序中DLL Hell的一个简单解决方案是静态链接所有库,即包含程序中所需的库版本,而不是选择具有指定名称的系统库。这在C / C ++应用程序中很常见,其中不必担心安装了哪个版本的MFC42.DLL,而是将应用程序编译为静态链接到相同的库。这完全消除了DLL,并且可以在仅使用提供静态选项的库的独立应用程序中实现,就像Microsoft基础类库一样。然而,牺牲了DLL的主要目的,即程序之间的运行时库共享以减少内存开销。在多个程序中复制库代码会导致软件膨胀,并使安全修复程序或更新版本的相关软件的部署变得复杂。
使用Windows文件保护(WFP)在Windows 2000中引入了DLL覆盖问题(微软称为DLL Stomping)。这可以防止未经授权的应用程序覆盖系统DLL,除非它们使用允许此操作的特定Windows API。可能仍存在Microsoft的更新与现有应用程序不兼容的风险,但通过使用并排程序集,当前版本的Windows通常会降低此风险。
第三方应用程序无法踩踏操作系统文件,除非它们将合法的Windows更新与其安装程序捆绑在一起,或者如果它们在安装期间禁用了Windows文件保护服务,并且在Windows Vista或更高版本中也取得系统文件的所有权并授予自己访问权限。 SFC实用程序可以随时还原这些更改。
这里的解决方案包括为磁盘和内存中的每个应用程序提供相同DLL的不同副本。
一个简单的手动解决方案是将问题DLL的不同版本放入应用程序的文件夹中,而不是通用的系统范围文件夹。只要应用程序是32位或64位,并且DLL不使用共享内存,这通常可以正常工作。对于16位应用程序,这两个应用程序不能在16位平台上同时执行,也不能在32位操作系统下的同一个16位虚拟机中同时执行。 OLE在Windows 98 SE / 2000之前阻止了这一点,因为早期版本的Windows为所有应用程序都有一个COM对象注册表。Windows 98 SE / 2000引入了一种称为并行程序集的解决方案,它为每个需要它们的应用程序加载单独的DLL副本(从而允许需要冲突的DLL的应用程序同时运行)。这种方法通过允许应用程序将模块的唯一版本加载到其地址空间中来消除冲突,同时保留通过使用内存映射技术在仍然执行的不同进程之间共享公共代码来共享应用程序之间的DLL(即减少内存使用)的主要好处使用相同的模块。然而,在多个进程之间使用共享数据的DLL不能采用这种方法。一个负面影响是DLL的孤立实例可能无法在自动化过程中更新[1]。
根据应用程序体系结构和运行时环境,可移植应用程序可能是减少某些DLL问题的有效方法,因为每个程序都捆绑了自己所需的任何DLL的私有副本。该机制依赖于应用程序在加载它们时没有完全限定依赖DLL的路径,并且操作系统在任何共享位置之前搜索可执行目录。然而,这种技术也可以被恶意软件利用,如果私有DLL没有以与共享DLL相同的方式保持最新的安全补丁,则增加的灵活性也可能以牺牲安全性为代价。
应用程序虚拟化还允许应用程序在“泡沫”中运行,这避免了将DLL文件直接安装到操作系统的文件系统中。