Click here to Skip to main content
15,881,938 members
Articles / Driver

EchoInst: An Example Wix Install of the WDK Echo Driver

Rate me:
Please Sign up or sign in to vote.
4.75/5 (3 votes)
9 Apr 2013CPOL7 min read 27.1K   337   10   3
This gives an example on how to install drivers using Wix, including the use of deferred custom actions.

Introduction

The article DevMSI: An Example C++ MSI/Wix Deferred Custom Action DLL describes creating a C/C++ DLL to provide support for deferred custom actions in Wix installs. This article builds on that foundation to provide a functional MSI install for the echo sample from the Windows WDK. It leverages DevMSI as needed for creation and removal of the non-PnP device 'root\echo', as well as removal of the service 'echo'. 

The WiX files and build scripts are available for download here

Background 

In order to install the Echo driver completely, several steps have to be accomplished:

  • Copy (and remove on uninstall) the driver and related application to an install directory. This task is accomplished through the use of WiX. 
  • Perform a driver file pre-install on the system. This does not actually set up the driver to be loaded: it merely does the necessary steps to inform Windows of the new driver and place it in appropriate system paths for discovery when the appropriate driver is detected. The MSDN article Driver Install Frameworks API (DIFxAPI) explains the API Microsoft provides for this task. In Wix V3 the API is available to the Wix framework. 
  • For installation, add the non-PnP device 'root\echo' to the system which will use the Echo driver. For removal, remove any non-PnP devices named 'root\echo' and also remove the echo service. For these tasks, the DevMSI dll will extend Wix with the needed functionality. 

Wix V3 and DIFxAPI 

Wix and DIFxAPI  is a larger topic than this article intends to address. However, Koby Kahane's excellent blog post "Installing filter drivers with DIFxApp and a WiX v3 MSI" is an excellent starting point if the reader wishes to understand more of what is being done through the .wxs scripts presented here. 

DevMSI 

As mentioned above, the companion article DevMSI: An Example C++ MSI/Wix Deferred Custom Action DLL describes the C++ DLL extension used to perform needed setup tasks. In brief, DevMSI performs a limited subset of the WDK devcon.exe sample:  this subset will add or remove devices and related services on request. 

Steps Required to build the MSI with WIX 

The following steps will need to be accomplished to deploy the MSI, in sequence. Sample build scripts are available for download, but the essential commands without error checking are listed here for reference. 

  • Build the WDK echo driver and sample application 
  • pushd %PROJECT_ROOT%\general\echo 
    build -ceZ 
    popd 
  • Copy the needed build outputs to a working directory
  • set COPY_SOURCE="%PROJECT_ROOT%"\general\echo\kmdf\AutoSync\obj%_BuildType%_%DDK_TARGET_OS%_%_BUILDARCH%\%_BUILDARCH%
    set COPY_TARGET="."
    copy %COPY_SOURCE%\echo.inf %COPY_TARGET%\.
    copy %COPY_SOURCE%\echo.sys %COPY_TARGET%\.
    set COPY_SOURCE="%PROJECT_ROOT%"\general\echo\exe\obj%_BuildType%_%DDK_TARGET_OS%_%_BUILDARCH%\%_BUILDARCH%
    copy %COPY_SOURCE%\echoapp.exe %COPY_TARGET%\.
    set COPY_SOURCE="%PROJECT_ROOT%"\..\redist\wdf\%_BUILDARCH%
    copy %COPY_SOURCE%\WdfCoInstaller01009.dll %COPY_TARGET%\.
  • Sign the driver and test executable - this example uses signtool with Verisign 
  • Signtool sign /v /ac "c:\verisign\VeriSign Class 3 Public Primary Certification Authority - 
       G5.cer" /i Verisign /t http://timestamp.verisign.com/scripts/timestamp.dll [filename] 
  • Create a .cat file from the INF file 
  • inf2cat /driver:.\ /os:7_X64 
    • Sign the .cat file - see above for sample usage 
    • Run the WiX tool candle on the .wxs files 
    • %WIX%\bin\candle -ext %WIX%\bin\WixUIExtension.dll -ext %WIX%\bin\WixUtilExtension.dll 
                    -ext %WIX%\bin\WixDifxAppExtension.dll *.wxs -arch x64  
    • Run the WiX tool light on the .wixobj files
    • %WIX%\bin\light -ext %WIX%\bin\WixUIExtension.dll -ext %WIX%\bin\WixUtilExtension.dll -ext 
          %WIX%\bin\WixDifxAppExtension.dll *.wixobj %WIX%\bin\difxapp_x64.wixlib -o echo.msi

All of the above are scripted into a set of .cmd files available for download, with "build.cmd echo" being an appropriate command line to start the sequence of steps.

Deferring a Discussion on Custom Actions 

This installer uses deferred custom actions to leverage DevMSI for the non-PnP device installation and removal. Since many types of drivers (PnP device drivers, filter drivers) do not require deferred custom actions to be installed (or removed), this article will first examine the echo.wxs file with emphasis on the non-custom action portions. 

Examining Echo.wxs 

WiX files are written in XML, and many examples are available with minimal searching. This file starts with much of the same boilerplate as those examples: 

XML
<?xml version='1.0' encoding='Windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'
     xmlns:difx='http://schemas.microsoft.com/wix/DifxAppExtension'> 

Note the extra namespace provided for DIFxAPP, as many non-drivers will not include the extension. Next follows some slightly tailored boilerplate defining the product:

XML
<Product Name='EchoInstaller' 
           Id='PUT-GUID-HERE' 
           Language="'1033'" Codepage='1252' Version='1.10' Manufacturer='CodeProject' 
           UpgradeCode='PUT-GUID-HERE'>
    <Package Id='*' Keywords='echo' Description='WDK Echo Sample Driver Installer'
             Comments='Installs WDK Echo Sample Drivers' Manufacturer='CodeProject' InstallerVersion='200'
             Languages='1033' Compressed='yes' SummaryCodepage='1252' />
    <Media Id='1' Cabinet='Installer.cab' EmbedCab='yes' DiskPrompt='WDK Echo Sample Driver Media' />
    <Property Id='DiskPrompt' Value='WDK Echo Sample Driver Install Media' /> 

Many of the fields described here can and should be customized for the particular driver installation. Next, the file describes the installation directory target path:

XML
<Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFiles64Folder'>
        <Directory Id='INSTALLDIR' Name='WDK Samples'> 

This example expects to have a base installation directory of "WDK Samples" under the Program Files folder (64-bit only, as this sample expects a 64-bit driver). Within that base folder, the installer will put the echoapp.exe test application and create another folder for the driver files: 

XML
<Component Id='SampleApp' Guid='PUT-GUID-HERE'>
<File Id='echoappEXE' Name='echoapp.exe' DiskId='1' Source='Driver\echoapp.exe' KeyPath='yes' />
</Component>
<Directory Id='DriverDir' Name='Drivers'> 

The .INF, .CAT, and .SYS files go in the driver folder, as does the KMDF co-installer. Note the extra XML that tells DIFxAPI these are the driver files to preinstall: 

XML
<Component Id='EchoDriver' Guid='PUT-GUID-HERE'>
          <File Id='echoSYS' Name='echo.sys' DiskId='1' Source='Driver\echo.sys' KeyPath='yes' />
          <File Id='echoINF' Name='echo.inf' DiskId='1' Source='Driver\echo.inf' />
          <File Id='echoCAT' Name='KmdfSamples.cat' DiskId='1' Source='Driver\KmdfSamples.cat' />
          <File Id='wdfcoinstaller01009DLL' Name='wdfcoinstaller01009.dll' DiskId='1' Source='Driver\wdfcoinstaller01009.dll' />
          <difx:Driver AddRemovePrograms="no" Legacy="no" PlugAndPlayPrompt="no" Sequence='1' />
        </Component>
      </Directory>
    </Directory>
  </Directory>
</Directory> 

Next, the file declares that the drivers and the sample app should be installed on a "complete" installation, and to use a stock simple UI for the installer:

XML
<Feature Id='Complete' Level='1'>
  <ComponentRef Id='SampleApp' />
  <ComponentRef Id='EchoDriver' />
</Feature>
<UIRef Id="WixUI_Minimal" /> 

Skipping the Custom Action related items for now, there's nothing left in the .WXS files but closing some tags:

XML
</Product>
</Wix> 

That's all that is necessary!  

Needed: Deferred Execution Custom Actions 

Adding a non-PnP device to the system requires Administrator privileges on the system. The MSDN article Deferred Execution Custom Actions includes this important note:

Note that deferred custom actions, including rollback custom actions and commit custom actions, 
   are the only types of actions that can run outside the users security context. 

Because DevMSI will need additional privileges, this means that a Deferred Execution Custom Action is necessary for all DevMSI tasks.  

Examining DevMSI.wxs 

The code necessary for wrapping DevMSI calls inside of Deferred Custom Actions is contained in DevMSI.wxs. This file does not contain any of the project setup and definition, but rather defines only the extensions that are needed. In WiX terms, this is a fragment: 

XML
 <?xml version='1.0' encoding='Windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
  <Fragment> 

A binary item needs to be defined, which denotes the WiX alias for the DLL ("DevMsi") and the path to the binary on the WiX computer. The binary will be extracted and used during setup as necessary by the MSI. 

XML
<Binary Id="DevMsi" SourceFile="..\DevMsi\x64\Release\DevMsi.dll" /> 

Next three Deferred Custom Actions are defined as available from the DevMsi binary 

XML
<CustomAction Id="AddDevice"
              BinaryKey="DevMsi"
              DllEntry="CreateDevnode"
              Execute="deferred"
              Impersonate="no"
              />

<CustomAction Id="DelDevice"
              Return="ignore"
              BinaryKey="DevMsi"
              DllEntry="RemoveDevnode"
              Execute="deferred"
              Impersonate="no"
              />
    
<CustomAction Id="DelService"
              Return="ignore"
              BinaryKey="DevMsi"
              DllEntry="RemoveService"
              Execute="deferred"
              Impersonate="no"
              /> 

For each of these, Id names the custom action for use elsewhere, BinaryKey is the alias name specified above, and DllEntry is the C entry point to the DLL. The Execute="deferred" keyword is what makes this custom action a deferred custom action, and Impersonate="no" allows the custom action to run with Admin privileges. 

Needed: (Normal) Custom Actions 

Defining a Deferred Execution Custom Action is relatively straightforward, as noted above. However, it is not immediately obvious how to pass parameters to the deferred custom action. According to the MSDN article Obtaining Context Information for Deferred Execution Custom Actions, there are very few properties (three) that can be retrieved during a deferred custom action.  The only property that is really usable to pass parameters to the action is the CustomActionData property.

The way to set the CustomActionData property for a deferred custom action is, surprisingly, through another custom action. This action does not need to be deferred since it does not need a privileged security context, and in fact probably should not be deferred so that it can in turn access other properties.

As a result, the way to call a deferred custom action with parameters goes through the following steps:

  • Define a Custom Action that will set the CustomActionData property for a Deferred Execution Custom Action.
  • Define a Deferred Execution Custom Action.
  • Schedule both the Custom Action and the Deferred Execution Custom Action to execute during an install or uninstall sequence as appropriate. 

This wxs example uses a simple naming convention to help the reader understand which custom actions are aligned with their corresponding deferred custom actions. If Foo is the ID of a deferred custom action, then Foo.SetParam is the ID of the corresponding custom action that sets CustomActionData for Foo. 

Revisiting Echo.wxs  

Looking back into echo.wxs at the latter part of the script reveals the custom actions:

XML
<CustomAction Id="AddDevice.SetParam"
          Return="check"
          Property="AddDevice"
          Value='"[DriverDir]echo.inf" root\Echo'
          />
<CustomAction Id="DelDevice.SetParam"
          Return="check"
          Property="DelDevice"
          Value="root\Echo"
          />
<CustomAction Id="DelService.SetParam"
          Return="check"
          Property="DelService"
          Value="Echo"
          /> 

As described above, AddDevice.SetParam sets the Custom Action Data for AddDevice to be the value -- in this case, the expanded string '"[DriverDir]echo.inf" root\Echo'. Looking back again at the directory paths earlier in the .wxs file finds the [DriverDir] property:

XML
<Directory Id='DriverDir' Name='Drivers'> 

So when the CustomAction is performed, DriverDir will be expanded to be the installation directory for the driver files, and the CustomActionData for AddDevice will be set to the appropriate string. When the deferred custom action is executed, the DLL will be able to extract and use this string as an input parameter.

Similarly, the other two custom actions listed above (DelDevice.SetParam and DelService.SetParam) set a string parameter for their associated deferred custom actions. Once the deferred custom actions and custom actions are all defined, the next step is to declare when these actions are to be performed:

XML
<InstallExecuteSequence>
  <RemoveExistingProducts After="InstallInitialize" />
  <Custom Action ="AddDevice.SetParam" After="InstallFiles">NOT Installed</Custom>
  <Custom Action='AddDevice' After="AddDevice.SetParam">NOT Installed</Custom>
  
  <Custom Action ="DelDevice.SetParam" After="AddDevice">Installed</Custom>
  <Custom Action='DelDevice' After="DelDevice.SetParam">Installed</Custom>
  <Custom Action ="DelService.SetParam" After="DelDevice">Installed</Custom>
  <Custom Action='DelService' After="DelService.SetParam">Installed</Custom>
</InstallExecuteSequence>

Note that the deferred custom action is set to run after the custom action that sets it's CustomActionData parameter. The NOT Installed text declares that the (deferred or normal) custom action is run during installation, and the Installed text declares that the custom action is to be run only if the product is already installed (presumably removal). There are many other possibilities and combinations that are not investigated in this article: WiX manuals and tutorials can provide additional information. 

Conclusion

Writing a WiX installer was actually easier than explaining how to do so. It is the author's hope that future users can leverage the information here to their advantage.

History

  • 2013-04-09: Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Seagate Technology
United States United States
Joe is a Sr. Software Engineer for Seagate Technology. He greatly enjoys practical applications of C/C++ and problem solving in general. He and his family live in a wonderful spot of the world named Longmont, CO (USA).

Comments and Discussions

 
Questionhow to install drivers with WIXUI_INSTALLDIR Pin
Ajaya NG10-May-15 20:23
Ajaya NG10-May-15 20:23 
Questionwxs files Pin
StephenSalsero12-Dec-13 17:59
StephenSalsero12-Dec-13 17:59 
GeneralMy vote of 5 Pin
Sergey Podobry25-Sep-13 22:41
professionalSergey Podobry25-Sep-13 22:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.