1

I've struggled through building a new WiX installer, and I'm really happy with it, except that I also need to install a device driver.

Ideally, I'd like to be able to detect the presence of this driver by using RegistrySearch, e.g.

<Property Id="DRIVERINSTALLED">
    <RegistrySearch Id="DriverInstalledSearch" Root="HKLM" Key="SOFTWARE\DriverCompany\Settings" Name="InstallPath" Type="raw" />
</Property>

If there is a value for the InstallPath registry key, then I'd like to move ahead with the device driver installation. I haven't figured that part out yet, but that is secondary right now, because I can't even get the device driver to install every time.

There are various examples floating around on the web, and the one corresponding to WiX v3.5 seems to follow this pattern:

<CustomAction Id="InstallDeviceDrivers" Execute="deferred" Directory="INSTALLLOCATION" ExeCommand="setup.exe" Return="check" />
.
.
.
<InstallExecuteSequence>
    <Custom Action="InstallDeviceDrivers" After="InstallFiles" />
</InstallExecuteSequence>

Whenever I run my msi, I get the error "a program required for this install to complete could not be run".

setup.exe is copied to my INSTALLLOCATION, and I have verified this by looking in that folder when my msi fails.

I played with the value of the After attribute, but InstallFiles seems to be the right one since it maintains elevated user privileges. I had originally tried InstallFinalize, but that failed and I figured that it wasn't running with elevated privileges. The problem is, none of the other Actions I have tried work.

I then ran my installer with msiexec /i installer.msi /l*v install.log and looked over the output file. That's where I saw a slightly more specific error:

MSI (s) (74:CC) [14:06:10:098]: Executing op: ActionStart(Name=InstallDeviceDrivers,,)
Action 14:06:10: InstallDeviceDrivers. 
MSI (s) (74:CC) [14:06:10:098]: Executing op: CustomActionSchedule(Action=InstallDeviceDrivers,ActionType=1058,Source=C:\Program Files\MyCompany\MyProduct\,Target=setup.exe,)
MSI (s) (74:CC) [14:06:10:108]: Note: 1: 1721 2: InstallDeviceDrivers 3: C:\Program Files\MyCompany\MyProduct\ 4: setup.exe 
MSI (s) (74:CC) [14:06:10:108]: Note: 1: 2205 2:  3: Error 
MSI (s) (74:CC) [14:06:10:108]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1721 
Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: InstallDeviceDrivers, location: C:\Program Files\MyCompany\MyProduct\, command: setup.exe 
MSI (s) (74:CC) [14:06:11:800]: Note: 1: 2205 2:  3: Error 
MSI (s) (74:CC) [14:06:11:800]: Note: 1: 2228 2:  3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1709 
MSI (s) (74:CC) [14:06:11:800]: Product: Installer -- Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: InstallDeviceDrivers, location: C:\Program Files\MyCompany\MyProduct\, command: setup.exe 

Based off of some searching, it sounded like I just needed to run as admin, but my installer already triggers UAC... and sure enough, running the installer from an elevated command prompt didn't help.

Can anyone recommend a next course of action for me to debug this? If you additionally have information on how to conditionally install based on the presence of a registry key, that would be really great, too. Thank you!

EDIT -- I have run my installer on Windows XP 32bit and Windows 7 32bit, and it fails on both. Yet another reason to believe it's not permissions-related.

EDIT #2 -- I don't know why I didn't try this before, but I changed from setup.exe to notepad.exe, and Notepad launched. So obviously the CustomAction works. I'll try again with Process Monitor to see where it's looking for setup.exe... or perhaps I just can't run an installer from within another installer?

Dave
  • 14,618
  • 13
  • 91
  • 145

2 Answers2

4

I'd like to revise my answer to cover what I have learned that is relevant to what I think is a typical scenario:

  • You want to customize the bitmaps in the dialogs
  • You also might want to skip the licensing agreement.
  • You want to detect presence of some prerequisite, like the .NET Framework 4.0.
  • You want to write an installer that can install some other software, like a device driver, at the end of the installation process.
  • You want to use the presence of a registry key to make the previously-mentioned checkbox invisible
  • You might need to include a merge module, like the VC++ 2010 redistributable

First things first:

  • All of the information is on the Internet. I just spent hours wading through info and trying stuff.
  • Be patient, and don't give up!
  • Don't just rely on Google. The WiX documentation (in the .chm) has an enormous wealth of information. I suggest that you search there as well, if not first.
  • Did I say it already, "be patient, and don't give up!"

Skeleton WXS

Each of the sections below only have the information directly related to themselves. At the very end of the answer, I will post a skeleton (partially complete) .wxs.

Detecting the .NET Framework 4.0

First, you'll need to add a project reference to WixNetFxExtension. Then add this XML to your <Product> node: (search .chm for ".NET")

<PropertyRef Id="NETFRAMEWORK40FULL" />
<Condition Message ="This application requires .NET Framework 4.0. Please install the .NET Framework and then run this installer again.">
    <![CDATA[Installed OR NETFRAMEWORK40FULL]]>
</Condition>

Customizing the Bitmaps

Found in Customizing Build-in WixUI Dialog Sets in the .chm. I only changed the following:

  • WixUIBannerBmp
  • WixUIDialogBmp

Just set them under the <Product> element:

<WixVariable Id="WixUIBannerBmp" Value="$(var.ProjectDir)\Bitmaps\mybanner.bmp"/>
<WixVariable Id="WixUIDialogBmp" Value="$(var.ProjectDir)\Bitmaps\mydialog.bmp"/>

I just create a Bitmaps folder in my WiX installer project in Visual Studio 2010.

Skipping the EULA Dialog & Setting the Installation Location

There are several built-in ways to define your installer's look and flow, but for those of us that are lazy, you'll only care about WIXUI_MINIMAL and WIXUI_INSTALLDIR. The former is only for the extremely lazy (this is what I originally sent to people!), but it doesn't allow the user to do anything but click Yes to install the program. It won't tell you that the installation is complete, either. WIXUI_INSTALLDIR strikes a nice balance, IMO. You get the typical welcome dialog, click Next through more typical dialogs, and get a Finish button at the end of the process.

Add a project reference to WixUIExtension, then use the following XML:

Installing Device Driver at the End

If you want to install a device driver at the end of your installation, I think the easiest way is to just ensure that the driver's setup.exe is copied to your installation location and then executed from there. At least, that's what I did. Then you just need to use a CustomAction:

But what if you want to allow the user to decide whether or not to install the driver? Then you do this:

<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Install device drivers when the installer exits." />

and your <UI> node will now look like this:

<UI>
    <UIRef Id="WixUI_InstallDir" />
    <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2">1</Publish>
    <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">1</Publish>
    <Publish Dialog="ExitDialog" Control="Finish" Order="1" Event="DoAction" Value="InstallDeviceDrivers">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
    <Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION" />
</UI>

The logic added above just checks to see if the checkbox is checked, and the "and NOT Installed" is there to ensure that this operation only runs when the installer is installing your application, not uninstalling.

Using a Registry Key to Control Availability of the Checkbox

Just add the following XML to Product:

<Property Id="DRIVERINSTALLED">
    <RegistrySearch Id="DriverInstalledSearch" Root="HKLM" Key="SOFTWARE\DriverCompany\Settings" Name="SomeRegistryKeyThatMustBePresentIfInstalled" Type="raw" />
</Property>

and then use <SetProperty> instead of <Property>, as previously shown:

How do I create a conditional property in WiX? (Almost like an If-Then)

<SetProperty Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Install device drivers when the installer exits." After="CostFinalize">
    <![CDATA[NOT DRIVERINSTALLED]]>
</SetProperty>

Merge Module

Finally, what if you need to add the VC++ 2010 Redistributable package? I wasn't sure of the best way to do this, and although my application worked when I did this (where it didn't before), I found it strange that Add/Remove Programs didn't mention anything about it actually being installed. So for this part, YMMV.

Since I run a continuous integration (CI) server that doesn't necessarily have Visual Studio 2010 installed, I put the merge module file into my WiX installer project. I made a folder called MergeModules and put it there. The file is located in Program Files\Common Files\Merge Modules, and is called Microsoft_VC100_CRT_x86.msm. Now put the following line in your <Directory> node:

How To: Install the Visual C++ Redistributable with your installer (WiX on SourceForge)

<Merge Id="VCRedist" SourceFile="$(var.ProjectDir)\MergeModules\Microsoft_VC100_CRT_x86.msm" DiskId="1" Language="0"/>

and the following line in your <Feature> node:

<Feature Id="VCRedist" Title="Visual C++ 10.0 Runtime" AllowAdvertise="no" Display="hidden" Level="1">
    <MergeRef Id="VCRedist"/>
</Feature>

Skeleton WXS

I'm providing an incomplete skeleton .wxs below. It's incomplete because I'm only providing the element names for the stuff that isn't directly associated with this post. The missing information is easy to find.

Community
  • 1
  • 1
Dave
  • 14,618
  • 13
  • 91
  • 145
1

May I suggest a different approach. Use a bootstrapper such as WiX Burn to check if the driver is installed and if not, install it as a prerequisite. Then launch your MSI installer. The WiX bundle would look something like:

<Fragment>
    <util:RegistrySearch 
        Variable="DriverInstalled"
        Root="HKLM,SOFTWARE\Microsoft\MyProduct\[UniqueId]\Setup"
        Key="InstallPath"
        Result="Exists" />

    <PackageGroup Id="DriverPackage">
      <ExePackage 
        SourceFile="Path_To_Driver\Setup.exe"
        InstallCondition="InstallPath" />
    </PackageGroup>
    <PackageGroup Id="MainMsi">
      <MsiPackage 
        SourceFile="Path_To_Msi\Installer.msi"
        After="DriverPackage" />
    </PackageGroup>
</Fragment>

Note: I did not test the above, but that should get you started if you go that route. If you're interested I would recommend looking up WiX bootstrapper bundles and Define Searches Using Variables explains how you can use the util extension to search the registry in your bootstrapper, similar to how you do it in the installer. This approach has the benefit of not mucking up your installer with custom actions.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BryanJ
  • 8,485
  • 1
  • 42
  • 61