有没有办法预先测试,如果一个Windows EXE将无法加载,因为缺少DLL?

问题描述:

如果您尝试在没有任何更新的情况下在Windows 8.1上安装vs2015可再发行组件,则它将无法安装。但是,它会进入安装过程足够远的地步,以至于guid位于注册表中,因此如果您运行一个程序来检查注册表中是否存在可再发行组件,那么您将通过该检查。有没有办法预先测试,如果一个Windows EXE将无法加载,因为缺少DLL?

如果您然后尝试运行使用vs2015编译的程序,该程序需要一些未能安装的dll,则会出现一个弹出窗口,提示“该程序无法启动,因为......”您知道该演练。

我正在处理有此问题的安装程序(与NSIS),我试图找到一种方法来检测DLL丢失的问题,然后运行.exe并获得弹出窗口。是否有任何我可以运行的命令行工具,或者任何我可以调用的NSIS函数,都会在发生此问题之前向我提出这个问题?

甚至可以检查vs2015可再发行组件是否安装正确? (而不必检查可再发行组件中是否存在每个文件,因为我不知道它们都是什么)。

寻找任何想法来解决整体问题,不一定要专门弄清这个问题检查。我希望可以有各种各样的方式来重新分配可能无法安装。

+0

*“如果您运行的程序检查注册表中是否存在可再发行组件”* - 您将必须与该程序的作者进行交谈,并请他们修复其错误执行。注册表不是一个公共编程接口。 – IInspectable

+0

使用'RegMon' for Windows或'Process Monitor'来嗅探注册表访问并查找消息之前访问哪个密钥。然后从安装程序中检查它,并在必要时将其删除,然后预安装可再发行组件。我更喜欢不使用基于DLL的运行时 - 有选项可以将(静态链接)嵌入到EXE中,并且不需要这样的重排。 – i486

+1

@IInspectable我想他想知道是否安装VS2015可再发行组件。 –

我认为VS2015是安装在System32中的.DLL文件,而不WinSxS文件,所以你也许可以只检查是否vcruntime140.dll & msvcp140.dll是$SysDir的版本之一。

如果你担心,这可能是部分安装,你可以看到,如果你可以加载它(假设你的安装程序正在安装的东西位数匹配):

!include LogicLib.nsh 
System::Call 'KERNEL32::LoadLibrary(t "$SysDir\msvcr100.dll")p.r0' 
${If} $0 P<> 0 
    DetailPrint "I was able to load the MSVC 2010 run-time DLL" 
${Else} 
    DetailPrint "Ooops" 
${EndIf} 

这可能被认为是一点点的黑客,但它可能已经足够满足您的需求。 Dependency Walker会告诉你要查找哪些DLL。

您也可以拨打MsiGetProductInfo与NSIS,如果你想:

!define MSVC2005_X86REDIST_PRODUCTCODE {A49F249F-0C91-497F-86DF-B2585E8E76B7} 
!define MSVC2008_X86REDIST_PRODUCTCODE {FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4} 
!define MSVC2010_X86REDIST_PRODUCTCODE {196BB40D-1578-3D01-B289-BEFC77A11A1E} 
!define MSVC2010SP1_X86REDIST_PRODUCTCODE {F0C3E5D1-1ADE-321E-8167-68EF0DE699A5} 
!define MSVC2010_AMD64REDIST_PRODUCTCODE {DA5E371C-6333-3D8A-93A4-6FD5B20BCC6E} 
!define MSVC2010SP1_AMD64REDIST_PRODUCTCODE {1D8E6291-B0D5-35EC-8441-6616F567A0F7} 

!define MSVCREDIST_PRODUCTCODE ${MSVC2010_X86REDIST_PRODUCTCODE} ; I don't have VS2015 redist installed on this machine so I could not test it. 
!include LogicLib.nsh 
System::Call 'MSI::MsiGetProductInfo(t "${MSVCREDIST_PRODUCTCODE}", t "ProductName", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0' 
${If} $0 == 0 
    DetailPrint "ProductName: $1" 
    System::Call 'MSI::MsiGetProductInfo(t "${MSVCREDIST_PRODUCTCODE}", t "AssignmentType", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0' 
    DetailPrint "AssignmentType: $1" 
    System::Call 'MSI::MsiGetProductInfo(t "${MSVCREDIST_PRODUCTCODE}", t "PackageCode", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0' 
    DetailPrint "PackageCode: $1" 
    System::Call 'MSI::MsiGetProductInfo(t "${MSVCREDIST_PRODUCTCODE}", t "VersionString", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0' 
    DetailPrint "VersionString: $1" 
${Else} 
    DetailPrint "Not registered with Windows Installer" 
${EndIf} 

This blog post说,Visual Studio 2005使用MsiQueryProductState而那也许是一个不错的简单的选择,如果你不需要任何更多的细节:

!define INSTALLSTATE_DEFAULT 5 
System::Call 'MSI::MsiQueryProductState(t "${MSVCREDIST_PRODUCTCODE}")i.r0' 
${If} ${INSTALLSTATE_DEFAULT} = $0 
    DetailPrint "Installed" 
${Else} 
    DetailPrint "Not installed" 
${EndIf} 
+0

很时髦,谢谢。 – stu

+0

Dependency Walker多年未更新,并产生不可靠的信息。它不知道任何有关转发器DLL的信息,例如,它们是为了将API表面划分为API集而引入的。 – IInspectable

+0

@IInspectable转发器DLL的含义是什么?它支持经典的“DLL.FuncName”转发器。它不懂API集合,但是那些没有记录的AFAIK(WRT它们的内部格式以及它们是如何使用的)。 – Anders