NetInverse Developers Blog

January 5, 2010
Category: WiX — Tags: , , , — admin @ 8:33 pm

If you have worked with MSI for a while, I bet you have encountered this: You have built and installed an MSI. Later on you change the WiX of it and build the new MSI, when you try to uninstall the old MSI with the new one, the uninstall fails.

In this case, you can

1) Use the utility tool MSIZap to clean up manually.
2) Or, do a force install with the new MSI using msiexec’s Repair Options. After that you can uninstall the MSI and go back to the good state.

msiexec /i /fe your_installer.msi

Repair Options
/f[p|e|c|m|s|o|d|a|u|v]
Repairs a product
p - only if file is missing
o - if file is missing or an older version is installed (default)
e - if file is missing or an equal or older version is installed
d - if file is missing or a different version is installed
c - if file is missing or checksum does not match the calculated value
a - forces all files to be reinstalled
u - all required user-specific registry entries (default)
m - all required computer-specific registry entries (default)
s - all existing shortcuts (default)
v - runs from source and recaches local package

April 4, 2009
Category: WiX — Tags: , , , — admin @ 11:46 pm

WiX supports reading enviromental variables, and has some useful preprocessors like ifdev. You can use your build script to set the environmental variables first. WiX compiler will substitute the environmental variables defined in your WiX file with the actual values you set.

For example:

    <?ifdef $(env.BUILD_TYPE) ?>
        <?define ROOT="$(env.APP_ROOT)\bins\$(env.BUILD_TYPE)"?>
    <?else?>
        <?define ROOT="$(env.APP_ROOT)\bins"?>
    <?endif?>
Category: WiX — Tags: , , , — admin @ 11:43 pm

What you need to do is write a VBS custom action to read the Xml configuration parameters and set them as MSI properties. See the customization VBS example below.

WiX CustomAction: SetProperties.vbs

    Sub SetProperties
       strXmlConfigFile = Session.Property("XMLCONFIG")
       Set objDoc = CreateObject("msxml2.DOMDocument.3.0")
       objDoc.Load(strXmlConfigFile)
       Session.Property("MYFUNKYPROP") = objDoc.selectSingleNode("/root/Parameter[@name='Something']").text
       Set objDoc = Nothing
    End Sub

WiX fragment example:

   <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
       <Fragment Id="SetProperties"> 

           <Binary Id="SETPROPERTIES" SourceFile="SetProperties.vbs" />
           <CustomAction Id="SetProperties" BinaryKey="SETPROPERTIES" VBScriptCall="SetProperties" Execute="immediate" />
           <InstallExecuteSequence>
               <Custom Action="SetProperties" After="LaunchConditions" />
           </InstallExecuteSequence>
       </Fragment>
    </Wix>
April 3, 2009
Category: WiX — Tags: , , , — admin @ 9:51 pm

Following sample wxs file demonstrates how to create an MSI to install a web site and virtual directory.

<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>
    <Product Id='6197b262-b2d8-464c-9d0b-6cade171b46f'
    Name='WixWebSiteExample'
    Language='1033'
    Version='0.0.0.0' Manufacturer='Corporation'>
        <Package Id='439d5627-cc07-4a41-9f50-b201ae3f8202'
                 Description='Creating a web site with WiX'
                 Comments='Creating a web site with WiX'
                 InstallerVersion='200'
                 Compressed='yes' />

        <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />

        <Directory Id='TARGETDIR' Name='SourceDir'>
            <Directory Id='ProgramFilesFolder' Name='PFiles'>
                <Directory Id='ApplicationFolder' Name='AppDir' LongName='Test Directory'>
                    <Component Id='WebSiteComponent' Guid='6b27e78e-bcbc-462a-bd7a-50cf991c7d39' DiskId='1'>
                        <File Id='WixExampleFile' Name='simple.txt' src='bin\simple.txt' />
                        <WebSite Id='DefaultWebSite'
                                 Description='My First Web Site Created With WiX'
                                 Directory='ApplicationFolder'>
                            <WebAddress Id="AllUnassigned" Port="80" />
                        </WebSite>
                    </Component>
                    <Component Id="WebVirtualDirComponent" Guid="8d7c59c0-b84d-40d9-b3a5-0c73b6487ae4">
                        <WebVirtualDir Id="VDir"
                                       Alias="Test"
                                       Directory="ApplicationFolder"
                                       WebSite="DefaultWebSite">
                            <WebApplication
                                       Id="TestWebApplication"
                                       Name="Test" />
                        </WebVirtualDir>
                    </Component>
                </Directory>
            </Directory>
        </Directory>

        <Feature Id='TestProductFeature' Title='Wix File Product Feature' Level='1'>
            <ComponentRef Id='WebSiteComponent' />
            <ComponentRef Id='WebVirtualDirComponent' />
        </Feature>
    </Product>
</Wix>

Then you need to compile your WiX file and link it.

         >candle website.wxs
         >light -out wixsite.msi website.wixobj sca.wixlib
         >msiexec /i wixsite.msi

Note that you need to link wixobj with sca.wixlib which is located at your wix\ca folder. Above sample shows you how to create anew web site. If you just want to create a virtual directory under an existing web site, you just need to move the WebSite element out of the Component node. If a new site specifies a port number that collides with the port used by an existing web site, the existing web site will be modified, otherwise a new site will be created.

Category: WiX — Tags: , , , — admin @ 9:50 pm

We will use sample product.wxs file again in this example

  1. Create an xml file: product.wxs (copied from wix.chm)  as below
    This wix file will create an MSI file to copy readme.txt into %sysdrv%/program files/test program/

    <?xml version='1.0'?>
    <WiX xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>
       <Product Id='12345678-1234-1234-1234-123456789012' Name='Test Package' Language='1033'
                Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
          <Package Id='12345678-1234-1234-1234-123456789012'
                    Description='My first Windows Installer package'
                    Comments='This is my first attempt at creating a Windows Installer database'
                    Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />
          <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
          <Directory Id='TARGETDIR' Name='SourceDir'>
             <Directory Id='ProgramFilesFolder' Name='PFiles'>
                <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
                   <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
                      <File Id='readme' Name='readme.txt' DiskId='1' src='readme.txt' />
                   </Component>
                </Directory>
             </Directory>
          </Directory>
          <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
             <ComponentRef Id='MyComponent' />
          </Feature>
          <InstallExecuteSequence>
             <Custom Action="test" Sequence='1'/>
          </InstallExecuteSequence>
          <Binary Id='Customization.vbs' src='Customization.vbs'/>
          <CustomAction Id='test' BinaryKey='Customization.vbs' VBScriptCall='Hello' Return='check'/>
       </Product>
    </WiX>
  2. Create a customization VBS code: Customization.vbs
        function Hello
            msgbox Session.Property("MYPARAM")
        end function

    In the above VBS customization code, Session.Property is being used to retrieve to properties defined in your MSI. If the property MYPARAM is not defined in your wxs file, you can directly it as a command line parameter like below. MSI will automatically append it into its property list.

  3. Compile:
    c:wixcandle product.wxs
  4. Link:
    c:wixlight product.wixobj
  5. Test your MSI:
    c:wixmsiexec /i product.msi MYPARAM="WiX is cool."

    You will see the setup code pops up a dialog with the message “WiX is cool.”

  6. You can control when the CustomAction should be run by using attributes: Before, After or Sequence. For example:
         <InstallExecuteSequence>
             <Custom Action="test" After='InstallFinalize'/>    Note: "test" will run after "InstallFinalize"
          </InstallExecuteSequence>     
  7. You can also specify a condition for a custom action that should only be run at install or uninstall:
          $ComponentName > 2 Note: run at install
          $ComponentName = 2 Note: run at uninstall
         <InstallExecuteSequence>
             <Custom Action="test" After='InstallFinalize'><![CDATA[ $MyComponent > 2 ]]> </Custom>
          </InstallExecuteSequence>
         
Category: WiX — Tags: , , , — admin @ 9:46 pm

Today I will show you how to log tracing information from your custom actions into your MSI’s log. We will still use the product.wxs sample. You can follow the steps below:

  1. Create an xml file: product.wxs (copied from WiX.chm)  as below
    This WiX file will create an MSI file to copy readme.txt into %sysdrv%/program files/test program/

    <?xml version='1.0'?>
    <WiX xmlns='http://schemas.microsoft.com/WiX/2003/01/wi'>
       <Product Id='12345678-1234-1234-1234-123456789012' Name='Test Package' Language='1033'
                Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
          <Package Id='12345678-1234-1234-1234-123456789012'
                    Description='My first Windows Installer package'
                    Comments='This is my first attempt at creating a Windows Installer database'
                    Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />
    
          <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
    
          <Directory Id='TARGETDIR' Name='SourceDir'>
             <Directory Id='ProgramFilesFolder' Name='PFiles'>
                <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
                   <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
                      <File Id='readme' Name='readme.txt' DiskId='1' src='readme.txt' />
                   </Component>
                </Directory>
             </Directory>
          </Directory>
    
          <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
             <ComponentRef Id='MyComponent' />
          </Feature>
          <InstallExecuteSequence>
             <Custom Action="test" Sequence='1'/>
          </InstallExecuteSequence>
    
          <Binary Id='Customization.vbs' src='Customization.vbs'/>
          <CustomAction Id='test' BinaryKey='Customization.vbs' VBScriptCall='Hello' Return='check'/>
       </Product>
    </WiX>
  2. Create a customization VBS code: Customization.vbs
                   Function LogInfo(msg) Dim rec
                        Set rec = Session.Installer.CreateRecord(1)
                        rec.StringData(0) = msg
                        LogInfo = Session.Message(&H04000000, rec)
                    End Function
    
                    Function Hello
                        LogInfo "Customization is being called here."
                    End function
  3. Compile:
    c:\WiX\candle product.wxs
  4. Link:
    c:\WiX\light product.WiXobj
  5. Test your MSI:
    c:\WiX\msiexec /i product.msi /L*v log.txt

Open log.txt, search “Customization is being called here” to locate your the tracing information. Now you can log your debugging information to the calling MSI’s log file for diagnostics.

Category: WiX — Tags: , , , — admin @ 9:45 pm

By default WiX installs your application files into a folder under “Program Files”. This is because normally you have a few lines of code like below:

      <Directory Id='TARGETDIR' Name='SourceDir'>
         <Directory Id='ProgramFilesFolder' Name='PFiles'>
            <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
               <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>

The above WiX example deploys your files to %SystemDrive%\Program files\Test Program. However, sometimes you don’t want this behavior and need to install files to an arbitrary folder. To achieve this, you can try these:

  • Don’t worry about TARGETDIR
  • Create a public property (like MYAPPPATH, or INSTALLDIR) to set the new application installation location
  • Add a Directory node with Id=”MYAPPPATH” and Name=”.”. Note: Name=”.” overrides the parent folder
      <Property Id="MYAPPPATH"><![CDATA[c:\mydir\]]></Property>             <!-- New property -->

      <Directory Id='TARGETDIR' Name='SourceDir'>
         <Directory Id='ProgramFilesFolder' Name='PFiles'>
            <Directory Id="MYAPPPATH" Name=".">                             <!-- Overrides the parent folder -->
               <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
                  <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>

Now you can have what you want, enjoy WiXing!

Category: WiX — Tags: , , , — admin @ 9:44 pm

Following sample wxs file demonstrates how to use CustomAction to invoke InstallUtil.exe to run your managed installer class.

<?xml version='1.0'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>
   <Product Id='12345678-1234-1234-1234-123456789012' Name='Test Package' Language='1033'
            Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
      <Package Id='12345678-1234-1234-1234-123456789012'
                Description='My first Windows Installer package based on InstallUtil.exe'
                Comments='This is my first attempt at creating a Windows Installer database'
                Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />

      <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />

      <Directory Id='SourceDir' Name='SourceDir'>
         <Directory Id='ProgramFilesFolder' Name='PFDir'>
            <Directory Id='TestDir' Name='TestDir' LongName='Test Program'>
               <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
                   <File Id='tryInstall' Name="try" LongName='tryInstall.exe' DiskId='1' src='tryInstall.exe' />
               </Component>
            </Directory>
         </Directory>
      </Directory>

      <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
         <ComponentRef Id='MyComponent' />
      </Feature>      

      <InstallExecuteSequence>
         <Custom Action='ManagedInstall' After="InstallFinalize" />
      </InstallExecuteSequence>

      <CustomAction Id="ManagedInstall"
            Directory='TestDir'
            ExeCommand='"[WindowsFolder]Microsoft.NETFrameworkv2.0.50727installUtil.exe" /LogToConsole=false tryInstall.exe'
            Return='check'>
      </CustomAction>
   </Product>
</Wix>
Category: WiX — Tags: , , , — admin @ 9:42 pm

What is Custom Action?

The Windows Installer provides many built-in actions for performing the installation process. Standard actions are sufficient to execute an installation in most cases. However, there are situations where the developer of an installation package finds it necessary to write a custom action. Custom action is a way to extend functionality of Windows« Installer. A developer can write customization code in a dll or script(like the sample below) for their specific requirements and tell Windows Installer to invoke the code at certain points during setup.

Create an xml file: product.wxs (copied from WiX.chm) as below. This WiX file will create an MSI file that copies readme.txt into “%sysdrv%/program files/test program/”.

<?xml version='1.0'?>
<WiX xmlns='http://schemas.microsoft.com/WiX/2003/01/wi'>
   <Product Id='12345678-1234-1234-1234-123456789012' Name='Test Package' Language='1033'
            Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
      <Package Id='12345678-1234-1234-1234-123456789012'
                Description='My first Windows Installer package'
                Comments='This is my first attempt at creating a Windows Installer database'
                Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />

      <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />

      <Directory Id='TARGETDIR' Name='SourceDir'>
         <Directory Id='ProgramFilesFolder' Name='PFiles'>
            <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
               <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
                  <File Id='readme' Name='readme.txt' DiskId='1' src='readme.txt' />
               </Component>
            </Directory>
         </Directory>
      </Directory>

      <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
         <ComponentRef Id='MyComponent' />
      </Feature>
   </Product>
</WiX>

Compile WiX file:

    c:>WiX\candle product.wxs

Link WiXObj into MSI:

    c:>WiX\light product.WiXobj

Test your first MSI:

    c:>WiX\msiexec /i product.msi

Create a customization VBS code: Customization.vbs

   function Hello
       MsgBox "Hello from customization VBS!"
   end function

By using VBS customization code you can achieve a lot of things, like creating an IIS VDIR, acling some resources, setting COM+, etc. Now you need to modify product.wxs to invoke the customization.vbs as below:

<?xml version='1.0'?>
<WiX xmlns='http://schemas.microsoft.com/WiX/2003/01/wi'>
   <Product Id='12345678-1234-1234-1234-123456789012' Name='Test Package' Language='1033'
            Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
      <Package Id='12345678-1234-1234-1234-123456789012'
                Description='My first Windows Installer package'
                Comments='This is my first attempt at creating a Windows Installer database'
                Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />

      <Media Id='1' Cabinet='product.cab' EmbedCab='yes' />

      <Directory Id='TARGETDIR' Name='SourceDir'>
         <Directory Id='ProgramFilesFolder' Name='PFiles'>
            <Directory Id='MyDir' Name='TestProg' LongName='Test Program'>
               <Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
                  <File Id='readme' Name='readme.txt' DiskId='1' src='readme.txt' />
               </Component>
            </Directory>
         </Directory>
      </Directory>

      <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
         <ComponentRef Id='MyComponent' />
      </Feature>

      
      <InstallExecuteSequence>
         <Custom Action="test" Sequence='1'/>
      </InstallExecuteSequence>

      <Binary Id='Customization.vbs' src='Customization.vbs'/>
      <CustomAction Id='test' BinaryKey='Customization.vbs' VBScriptCall='Hello' Return='check'/>
   </Product>
</WiX>

We add a Binary node to specify the Binary data used for CustomAction elements, and added CustomAction node to specify a custom action to be added to the MSI CustomAction table. Various combinations of the attributes for this element correspond to different custom action types. For more information about custom actions see the MSDN documentation http://msdn2.microsoft.com/en-us/library/aa372048.aspx for a “Summary List of All Custom Action Types”.

Finally we add a Custom node to sequence a custom action. Since Custom must be a child node of AdminExecuteSequence, AdminUISequence, AdvertiseExecuteSequence, InstallExecuteSequence, InstallUISequence, thus we add a InstallExecuteSequence.

Compile again:

    c:>WiX\candle product.wxs

Link again:

    c:>WiX\light product.WiXobj

Test your MSI again: now you will see a pop window coming from your WiX customization(CustomAction) code.

    c:>WiX\msiexec /i product.msi

Similiarly, you can use custom actions to create/update or start/stop your COM+ applications.

    Function ResetCOMPlus
        Set objApplicationsAdmin = CreateObject("COMAdmin.COMAdminCatalog")
        objApplicationsAdmin.ShutdownApplication "OrderCheck"
    End function
Category: WiX — Tags: , , , — admin @ 9:39 pm

If your MSI doesn’t work properly, how do you troubleshoot? The easiest way is to turn on tracing to get the setup log file. The command line is:

            msiexec /i product.msi /L*v log.txt

‘L’ means generating a log file; ‘v’ means verbose. Open log.txt file, you can see detailed information of the setup process. For example, if you run product.msi like below:

            msiexec /i product.msi MYPARAM=cool /l*v "c:log.txt"

Open log.txt, you will see MYPARAM has been added to the properties:

     MSI (c) (D8:58) [01:12:36:093]: PROPERTY CHANGE: Adding MYPARAM property. Its value is 'cool'.
     ...
     Property(S): MYPARAM = cool

For detailed command line options of msiexec, you can check out MSDN.

Older Posts »

©2009 NetInverse. All rights reserved. Powered by WordPress