c++windowswinapinsisredistributable

Is there a way to test beforehand if a windows exe will fail to load because of missing dlls?


If you try and install the vs2015 redistributable on windows 8.1 without any updates, it will fail to install. But it will get far enough into the install process that the guid is in the registry so if you run a program that checks for the existence of the redistributable in the registry, you'll get past that check.

If you then try and run a program compiled with vs2015 that requires some of the dlls that failed to install, you'll get a popup that says "The program can't start because..." you know the drill.

I'm working on an installer (with NSIS) that is having this problem, and I'm trying to find a way to detect the dll missing problem before I run the .exe and get the popup. Is there any command line tool I can run or any NSIS function I can call that will clue me into this problem before it happens?

Or even a way to check if the vs2015 redistributable installed correctly? (without having to check for the existence of every single file in the redistributable, because I don't know what they all are.)

Looking for any ideas to solve the overall problem, not necessarily to specifically get past this one check. I expect there can be all sorts of ways the redistributable can fail to install.


Solution

  • I believe VS2015 is one of the versions that installs the .DLLs in System32 without WinSxS so you could perhaps just check if vcruntime140.dll & msvcp140.dll is in $SysDir.

    If you are worried that it might be a partial install you could see if you can load it (assuming your installer matches the bitness of the thing you are installing):

    !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}
    

    This might be considered a bit of a hack but it might be enough for your needs. Dependency Walker will tell you which DLLs to look for.

    You can also call MsiGetProductInfo with NSIS if you wish:

    !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 says that Visual Studio 2005 uses MsiQueryProductState and that is probably a nice simple alternative if you don't need any more details:

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