2

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.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
stu
  • 8,461
  • 18
  • 74
  • 112
  • *"if you run a program that checks for the existence of the redistributable in the registry"* - You're going to have to talk to the author of the program and kindly ask them to fix their buggy implementation. The registry is not a public programming interface. – IInspectable May 30 '17 at 14:21
  • Use `RegMon` for Windows or `Process Monitor` to sniff Registry access and find which key is accessed before message. Then check it from your installer and if necessary remove it and then pre-install the redistributable. I prefer not to use DLL based runtime - there is option to embed (static link) to your EXE and there is no need from such redist. – i486 May 30 '17 at 14:26
  • 1
    @IInspectable I think he want's to know if the VS2015 redistributable is installed or not. – Jabberwocky May 30 '17 at 14:27
  • @MichaelWalz: Correct, and the registry is not the place to look for that information. [MsiGetProductInfo](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370130.aspx) and friends are the way to go. – IInspectable May 30 '17 at 14:33
  • https://stackoverflow.com/a/34209692/12386 seems pretty popular to me... – stu May 30 '17 at 15:13
  • @i486 there's a way to statically link the runtime and all dependencies? Please do tell. – stu May 30 '17 at 15:13
  • In project Properties go to C++ / Code Generation / Runtime Library and select Multithreaded (`/MT`). Other options are MT with DLL and Debug / Debug DLL. This is on older VS but I think it will exist in 2015 as well. – i486 May 30 '17 at 18:30
  • @stu: You see, popularity and correctness are unrelated concepts. Have a stroll through the [tag:php] area to find lots and lots and lots of popular wrongness. – IInspectable May 31 '17 at 08:42
  • @i486 ahhh that yeah. That doesn't work. I mean it works but what happens is that as soon as you run statically linked binary on a machine who's library is a teeny weeny bit different, everything goes boom. You pretty much have to link dynamically. At least that's been my experience. – stu Jun 02 '17 at 00:45
  • @stu If you are using MFC or similar, ok. But for pure Win32 API and C standard lib I have never found a problem with newer or older Windows version. Are you sure that statically linked software does not have other problems? – i486 Jun 02 '17 at 08:27
  • well... we have zero problems with this one binary dynamically linked running on xp, win7, 8, 10, server 2012, and 2016 and probably a few others I don't know about, so I have a feeling it's not the application. – stu Jun 02 '17 at 14:27

1 Answers1

3

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}
Anders
  • 97,548
  • 12
  • 110
  • 164
  • Dependency Walker hasn't been updated in years, and produces unreliable information. It doesn't know anything about the forwarder DLLs, for example, that were introduced to enable the segregation of the API surface into API sets. – IInspectable May 31 '17 at 08:46
  • @IInspectable What do you mean by forwarder DLLs? It supports the classic "DLL.FuncName" forwarders. It does not understand API sets but those are undocumented AFAIK (WRT their internal format and how they are used). – Anders May 31 '17 at 12:52
  • The *api-ms-win-\*.dll* modules are API set forwarders. See [Introducing the Universal CRT](https://blogs.msdn.microsoft.com/vcblog/2015/03/03/introducing-the-universal-crt/) for a bit more details. – IInspectable May 31 '17 at 14:41
  • @Anders is it not required to quote `{A49F249F-0C91-497F-86DF-B2585E8E76B7}` in `!define MSVC2005_X86REDIST_PRODUCTCODE {A49F249F-0C91-497F-86DF-B2585E8E76B7}`? – Damilola Olowookere Jun 27 '17 at 14:57
  • 1
    @OlowookereEmmanuel No, if it required quotes I would have quoted it obviously. Only spaces and quotes need quotes... – Anders Jun 27 '17 at 15:09