【发布时间】:2011-03-24 22:21:05
【问题描述】:
我们有一个使用 Crystal Reports XI 生成打印报表的旧版 VB6 应用程序。我们通过经验发现,如果 Crystal Reports 打印引擎选择了错误版本的 usp10.dll(Windows Uniscribe 库),它就会崩溃。
一位客户的 Windows 7 机器(运行 Windows 7 Enterprise,32 位)一直存在打印问题。但是,我们还有一些其他客户在运行不同版本的 Windows 7 时没有遇到任何问题。
在其中一台出现打印问题的机器上,我注意到文件夹 C:\Program Files\Common Files\Microsoft Shared\Office10\ 中有旧版本的 usp10.dll(与 Crystal Reports XI 不兼容)。我不确定是什么应用程序安装了这些文件,因为客户没有安装 Office 2002(所以我假设另一个应用程序安装了它们)。但是,我暂时重命名了该文件,并且我们的应用程序能够正确打印,因此我们的应用程序似乎最初加载了该版本的文件,这导致了崩溃。
崩溃仅发生在用户尝试打印报告的那一刻。我们的应用程序直接依赖于 craxdrt.dll(Crystal Reports ActiveX 设计器运行时库)和 crviewer.dll(Crystal ActiveX 报表查看器库),无论是否发生崩溃我们直接通过 craxdrt.dll 或通过 Report Viewer 控件进行打印。
过去,我们通过将已知良好版本的 usp10.dll 复制到我们的应用程序目录并创建 .local 文件来启用 DLL 重定向来解决此问题.在客户站点,我尝试了这个,还尝试了为我们的 EXE 创建一个 .local 文件夹并将 usp10.dll 放在那里的替代方法,但两种方法都没有在我连接的机器上工作。
我确实注意到 usp10.dll 是 Windows 中的“已知”DLL(它在 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\KnownDLLs 中有一个条目),但我在另一台 Windows 7 机器上测试了我们的应用程序(运行专业版, 32-bit) 这里也将 DLL 列为注册表中的已知 DLL,通过使用 Dependency Walker,我可以看到重定向在该计算机上工作。这有点令人困惑,因为 Microsoft documentation 声明无法重定向已知 DLL。此外,正如我在问题标题中所暗示的那样,我们的主 EXE 不使用清单文件(Microsoft 文档指出存在嵌入式或独立清单会禁用 DLL 重定向)。
所以,我的问题是,是否有任何其他原因导致 DLL 重定向可以在某些机器上工作而不在其他机器上工作,这与 Windows 7 和 Windows XP 之间的差异有什么关系吗?我曾考虑删除KnownDLLs 注册表项中的所有内容,但由于重定向是在具有相同KnownDLLs 集合的机器上运行的,我不确定这是否会真正解决问题,我不知道如果我不需要,想删除那个键。我还没有机会再次连接到客户的机器以运行 Dependency Walker,但我不确定我是否能够解释它的日志(即使在它所在的机器上)工作时,我看到很多 LoadLibrary 调用 usp10.dll 指向重定向文件夹以外的文件夹,但有些调用显然被重定向了,所以我不确定这也意味着什么)。
编辑:我还应该提到,我们检查过的每台计算机在System32 文件夹中也有另一个usp10.dll 副本。查看 Chris 的 answer 和拉里·奥斯特曼 (Larry Osterman) 的 this blog post 对已知 DLL 如何工作的更多解释,我意识到这可能根本不是问题的原因,因为我们的程序没有加载 @987654331 的副本@ 即在 System32 文件夹中。
编辑#2:在我的 VB6 开发机器 (Windows XP SP3) 上玩了一些 Dependency Walker 之后,打印一直有效,我能够收集一些信息。我在 Dependency Walker 中分析了我们的应用程序并将其设置为记录完整路径名,看起来 Crystal Reports 依赖项之一(另一个 Crystal Reports DLL)试图加载 usp10.dll 来自多个(硬编码)路径,然后放弃并仅通过文件名询问。事实证明,它首先尝试从 Crystal Reports bin 文件夹加载它,然后尝试从 C:\Program Files\Common Files\Microsoft Shared\Office10\usp10.dll 加载它。如果在任一位置都找不到它,它只会向 Windows 询问usp10.dll(它将获取System32 中的那个)。但即使这样也不一致。有时它会在 Office10 文件夹中请求文件,然后似乎忽略了它找不到文件的事实,而其他时候有一系列 LoadLibrary 调用看起来像Crystal Reports 代码正在积极寻找不同位置的文件的备用副本。更令人困惑的是,至少有一个 Crystal Reports 组件看起来实际上在加载时依赖于 usp10.dll,因此该组件似乎总是在 System32 中获取副本。
我仍然不是 100% 清楚 为什么.local 重定向在这种情况下在此客户的计算机上不起作用,但我认为这部分解释了为什么这个特定客户遇到问题,因为所有有问题的计算机都有一个Office10 文件夹,其中包含一个明显不兼容的usp10.dll 版本。
但是,再一次,我仍然有一个基本问题:如果这些组件在这么多不同的地方寻找这个文件,我怎么能保证它们都将使用 相同复制?
【问题讨论】:
-
在出现故障的计算机上 - system32 中是否有 usp10.dll 的副本?我可以将这种情况理解为: 1. 因为 usp10.dll 被列为已知 dll,我们禁用了 .local 重定向器。 2. 如果此时usp10.dll不在system32中,当系统尝试为某个进程加载KnownDLLs时,找不到usp10.dll,后续尝试使用显式路径加载不兼容版本成功。解决这个问题应该是:如果丢失,请在 system32 中安装一个副本。
-
@Chris:失败的计算机除了 Office10 文件夹中的副本之外,还具有 system32 中的 usp10.dll 副本。据我所知,system32中的那个没问题,Office10文件夹中的那个和Crystal不兼容,因为如果我把那个重命名为usp10.dll_,比如Crystal Reports突然开始工作。令人困惑的是,USP10.dll 被列为故障计算机上的已知 DLL,但 Crystal Reports 结合了对 usp10.dll 的隐式和显式依赖,我认为这使事情变得更加复杂.
-
根据我刚刚找到的另一个 MSDN 页面,KnownDLLs 注册表项仅影响隐式加载的 DLL,而不影响使用
LoadLibrary加载的 DLL。根据Dependency Walker(在我的Windows 7虚拟机上运行程序),一些Crystal组件加载System32版本(因为它们对usp10.dll有加载时依赖,即隐式链接),一些LoadLibrary调用也加载System32副本也是如此,但其他 LoadLibrary 调用会继续在 Office10 中加载副本。它将两个 DLL 物理加载到进程中(LoadLibrary 返回 2 个不同的句柄)。 -
对于上述测试,我通过创建一个虚拟的 Office10 文件夹并将 usp10.dll 的副本放在那里(并将原始文件留在 system32 中)来模拟客户环境。在这种情况下,DLL 总是被加载两次。更糟糕的是,至少有一个使用usp10.dll 的Crystal DLL 似乎调用了DLL 的每个 副本中的函数。所以当Office10 DLL实际上是一个不同的版本时,它仍然会导致Crystal崩溃,因为它仍然会使用那个版本的DLL进行某些函数调用,即使它已经在system32中加载了那个......
-
另外,在 Windows XP 上,
LoadLibrary记录了C:\Program Files\Common Files\Business Objects\3.0\bin\usp10.dll的调用,所以我想我可以通过将 usp10.dll 的副本放在该文件夹中来解决问题。但是,当在 Windows 7 上运行相同的测试时,没有一个LoadLibrary调用在该文件夹中查找(它们在 Office10 文件夹中查找)。这个文件夹不在我的 XP 机器上的PATH中,所以我不知道为什么它在 XP 中而不是在 Windows 7 中,否则我认为把它放在那个文件夹中可以解决这个问题。跨度>