Search code examples
wixwix3wix-extension

WiX3 - util:XmlFile element executes again when new user first uses a per-machine installation


i created WiX installer project for deploying my .net winform app on a customer machine. The app only scans documents and saves the images to database on a server. The scanner is quite specific and only one in the company, but there are approx. four users that can occasionaly use it => app will be installed just on a single workstation dedicated only for scanning - most of the time it will be free and any of these users can come, scan the docs and go continuing his work.

=> i am doing a per-machine installation: ALLUSERS is hardcoded to 1.

Because the database servers in production are not controlled by me and i do not really know, where the database will be stored, i can not pack correct ConnectionStrings.config file to the MSI archive. Instead of it the setup modifies this config according to parameter values provided by the user during install. For updating the connection strings I use util:XmlFile element. The connectionstrings.config file is stored in installation directory together with app binaries.

Everything seemed to work fine, until I simulated two users using this per-machine installation. I have executed my wix setup project under my own account, the XML config file had been correctly updated and then I launched the application and tested the connection strings are ok. Everything was fine.

Then I switched to another user account. The shortcut was already present in the program menu - just as I would have expected since the installation is per-machine. So I clicked the shortcut and then (unexpectedly for me) a progress bar window "Wait until the configuration of product XY is finished." appeared. (Note that my machine locale is not english, the message would probably be slightly different on an english locale workstation). After few seconds the window disappeared and my application launched. Unfortunately it was not able to connect to the database, since the connectionStrings.config file has been rewritten - the connection strings have been updated using default (=incorrect) property values.

I have been investigating why the setup launchs again whenever new user-account tries to use it. It is because of the shortcut element (Shortcut is placed to 'ProgramMenuFolder'. There is a request for uninstall action, which AFAIK requires a parent Component and this Component needs a KeyPath, which has to be a registry key under HKCU.). When I remove all the Program-Menu-Shortcut-stuff from WXS, MSI is not launched again after switching user context.

Result is that I have setup program, which is able to configure connection to a database according to input parameters. But any later attempt to use the app from a second user-account just sends this configuration down the toilette. In production environment this would mean, that an administrator has to come and manually change the connection strings every time new user tries to use the app, which is of course unacceptable behavior.

This is simplified version of my WiX source:

    <?define ProductID = "11111111-1111-1111-1111-111111111111" ?>
    <?define ProductName = "MyProduct" ?>
    <?define ProductLocalName = "MyLocalLanguageProductName" ?>

    <!-- application's root registry path, where it stores its settings -->
    <?define ApplicationRootRegistryKey = "Software\MyCompany\MyProject\MyBuildConfiguration" ?>

    <Product Id="$(var.ProductID)" UpgradeCode="{11111111-1111-1111-1111-111111111112}"
             Name="$(var.ProductName)" Version="1.10.1103" 
             Manufacturer="MyCompany"Language="1029" Codepage="1250">

    <Package Id="*" InstallerVersion="200" Compressed="yes"
             Description="$(var.ProductName) Installer" Languages="1029" 
             SummaryCodepage="1250" />

        <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

        <!-- always install the app for all users -->
        <Property Id="ALLUSERS" Value="1"/>

        <!-- initialize properties used for adjusting connection strings.
             The user will provide valid property values through command-line -->
        <Property Id="DB_SERVER_NAME" Value="please-specify-db-server-name"/>
        <Property Id="DB_NAME" Value="please-specify-db-name"/>

        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder" Name="PFiles">
                <Directory Id="CompanyProgramFilesFolder" Name="CompanyName" >
                    <Directory Id="INSTALLDIR" Name="ProjectName">
                        <Directory Id="InstallDirApp" Name="Bin" />
                    </Directory>
                </Directory>
            </Directory>
            <Directory Id="ProgramMenuFolder" Name="Programs">
                <Directory Id="AppProgramMenuDir" Name="$(var.ProductLocalName)">
                    <Component Id="ProgramMenuDir" Guid="*">
                        <RemoveFolder Id='AppProgramMenuDir' On='uninstall'/>
                        <RegistryValue Root='HKCU' Key='$(var.ApplicationRootRegistryKey)' Type='string' Value='' KeyPath='yes' />
                    </Component>
                </Directory>
            </Directory>
        </Directory>

        <DirectoryRef Id="InstallDirApp">
            <Component Id="Executable" Guid="*">
                <File KeyPath="yes" Source="$(var.MyProject.TargetPath)">
                    <Shortcut Id="ProgramMenuShortcut" Name="$(var.ProductLocalName)"
                              Directory="AppProgramMenuDir" Advertise="yes"
                              WorkingDirectory="InstallDirApp" Icon="AppIcon.ico" IconIndex="0"/>
                </File>
            </Component>

            <!-- ConnectionStrings config file deployment and settings adjustment -->
            <Component Id="ConnectionStrings.config" Guid="*">
                <File KeyPath="yes" Source="$(var.Csob.ChequesScanning.SmartShell.TargetDir)ConnectionStrings.config" />
            <!--</Component>

            <Component Id="xml01" Guid="*">-->
                <!--<Condition><![CDATA[NOT Installed]]></Condition>-->

                <!-- this sets the connection strings according to provided parameters -->
                <util:XmlFile Id="SetConnectionString" Action="bulkSetValue"
                              File="[#ConnectionStrings.config]"
                              ElementPath="//add" Name="connectionString"
                              Value="Data Source=[DB_SERVER_NAME];Initial Catalog=[DB_NAME];Integrated Security=True;Pooling=True"
                              Permanent="yes" />
            </Component>
        </DirectoryRef>

        <Icon Id="AppIcon.ico" SourceFile="$(var.MyProject.ProjectDir)Resources\AppIcon.ico" />


        <Feature Id="ProductFeature" Title="MyProjectName" Level="1">
            <ComponentRef Id="Executable" />
            <ComponentRef Id="ConnectionStrings.config"/>
            <ComponentRef Id="ProgramMenuDir" />
        </Feature>

    </Product>
</Wix>

I have tried these steps to resolve the problem, but nothing helped me:
1) I have separated the and the to independent components.
2) I Have Tried adding a NOT INstalled under these components.
3) I have tried writing a registry value to HKLM during installation. I have added a RegistrySearch and Property for that registry value and then used that value as a condition (in fact just a replacement of "NOT Installed" from the previous)

Can anyone help with this? What am I doing wrong?

Thanks in advice

Marek


Solution

  • Root your registry key under HKMU (See reference). This will correctly root your registry key in either HKLM or HKCU depending on the value of the ALLUSERS property.