Jim
Jim

Reputation: 6881

How does MsiQueryProductState determine VC++ is installed

This might sound off topic, but bear with me please because I think it's a programming question at its core.

I have an installer for a really old application that I need to get installed on my Windows 8.1 PC. The installer does a check for Visual C++ 2005 SP 1, and if you don't have it, it prompts you to install it, and it redirects you to download it, except that download page redirects to a Microsoft 404.

I have VC++ 2005 SP 1 installed with both x86 and x64.

So, I decompiled the installer using JustDecompile, and the installer was in WinForms and used this code below to check for VC++ 2005 SP 1.

Basically if this line of code was true, it would show a message that you need to get VC++.

if (Form1.MsiQueryProductState("{7299052B-02A4-4627-81F2-1818DA5D550D}") != 5)

I've read about it and that was an acceptable way to check for VC++ 2005 in the time of the dinosaurs, but I believe that GUID above which is a product code is no longer accurate for Win 8.1 because I've gotten VC++ 2005 SP 1 directly from Microsoft and it's still not finding it.

Now, I'd like to figure out how to work around this problem. Does anyone know how the method MsiQueryProductState determines if you have something installed for a given product code? Does it look in the registry somewhere? If so, I could maybe work around this by faking it in the registry or something.

Any other suggestions appreciated!

Edit to answer questions from comments

From what I can tell when I decompiled this installer, it's a custom WinForms application. It's an EXE, not an MSI. It has a single form named Form1, which displays a list of products you can install. You select which products you want and click the Install button, and on clicking the Install button it calls a method that first checks for VC++.

It checks for VC++ with the following code, which has a hard coded product code for VC++. If the return value of MsiQueryProductStatus != 5, then it prompts you that you have to install VC++ and won't let you continue.

Form1.MsiQueryProductState("{7299052B-02A4-4627-81F2-1818DA5D550D}") != 5

And here's how Form1 defines MsiQueryProductState.

[DllImport("msi.dll", CharSet=CharSet.None, ExactSpelling=false)]
private static extern int MsiQueryProductState(string szProduct);

So I need to figure out either how to get VC++ with the product code 7299052B-02A4-4627-81F2-1818DA5D550D installed, or else I need to figure out how to trick MsiQueryProductState into thinking I have the VC++ with that product code installed.

Upvotes: 2

Views: 948

Answers (1)

Stein Åsmul
Stein Åsmul

Reputation: 42216

Note: Even when you find a way to bypass the check in question, the applications you install could fail to run without the correct runtime. You should be fine since this runtime is side-by-side with potential version redirection (backwards compatible 2.0). Just install the latest version - and run Windows Update! (further security fixes).


Dummy Setup: I wonder if you could make a dummy setup that has that product code hard coded in it, and then install it as a way to fool this check of yours. This is in effect spoofing - without malice. This approach is not great, but would be preferable to hacking the registry (which causes problems in almost all cases involving some complexity). Problems can still result if you forget to uninstall it or install it on top of an existing installation. I would definitely not use this for large scale deployment.

Mock-Up: I don't want to recommend this "solution" too much - it is kind of crazy, but here is a mock-up you can try to adapt and test using the WiX toolkit. You can also make a similar MSI using other tools to create MSI files. You could even just change a small MSI - using Orca - to use the product code you need and install it - then your setup should pass the checker. Make sure that MSI is not already installed - and uninstall it afterwards - yes, all quite mad and only to be used to remove the problem - not to use going forward:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
 <Product Id="{7299052B-02A4-4627-81F2-1818DA5D550D}" Name="DummyRuntime" Language="1033"
          Version="1.0.0.0" Manufacturer="Someone" UpgradeCode="GUID-HERE">
  <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

  <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
  <MediaTemplate EmbedCab="yes" />

  <!-- <UIRef Id="WixUI_Mondo" /> - standard GUI removed -->
  <Feature Id="ProductFeature" Title="DummyRuntime" Level="1" />

  <Directory Id="TARGETDIR" Name="SourceDir">
   <Component Feature="ProductFeature">
    <RegistryKey Root="HKLM" Key="Software\DummyRuntime">
      <RegistryValue Name="Flag" Value="1" Type="string" KeyPath="yes" />
    </RegistryKey>
   </Component>
  </Directory>

 </Product>
</Wix>

Technical Note: If you could find the UpgradeCode for this version of the runtime, you could insert that as the UpgradeCode and make a version check in the Upgrade table that would prevent installation if the real runtime is installed (it would be detected by sharing the same upgrade code, and then you can configure the upgrade table to abort). Any other upgrade code GUID should work since the MsiQueryProductState method just checks product code I believe.

Upvotes: 1

Related Questions