Thierry
Thierry

Reputation: 6458

Copy an external file based on a condition with wix installer

I have a couple of radio buttons on my wix dialog. When selecting the first one I enter info directly via the dialog but when selecting the second option, the user has to select where the config file is located on the network, so I want to copy this file to the target location but only if the second radio button has been selected and the file has been selected by the user.

So this question should really broken down in 3 parts:

1) How do I copy an external file from the network to the target location

2) How do I copy this file only if both the second radio button and the file has been set.

3) If not doable, other suggestions?

Note that my radio button selection is stored in a variable called CONFIGURATIONSELECTION and the selected file is stored in a variable called FILEPATH.

I thought the following would have worked but

  <Component Id="CMP_Startup.Config" Guid="0C301558-1894-4A7C-A0A4-C47660F9D334">
    <Condition>
      <![CDATA[(CONFIGURATIONSELECTION = 2 AND FILEPATH <> "")]]>
    </Condition>
    <File Id="FILE_Startup.Config" Source="FILEPATH" Compressed="no" />
  </Component>

but it's throwing an error at compile i.e. "The system cannot find the FILEPATH"

Can I even do this? If not, should I be using an CustomAction to copy the file manually? If so, how can I do this as the very last step of my install?

Thanks.

Upvotes: 0

Views: 2681

Answers (2)

Thierry
Thierry

Reputation: 6458

I ended up using the CustomAction method. Hope this helps someone else

Remember that:
- I use radio buttons and the selection is stored in the CONFIGURATIONSELECTION variable.
- I use a custom action that allows the user to select a file via a file selection dialog box and I store the returned result in the FILEPATH variable.

First, take care of the variable definition:

<Product>
    ...
    <Property Id="CONFIGURATIONSELECTION" Value="1" Secure="yes"/>
    <Property Id="FILEPATH" Admin="yes" Secure="yes" />
    ...
</Product>

Then define the CustomAction:

<Product>
...
    <Binary Id="CustomActions.CA" SourceFile="..\CustomActions\bin\
     $(var.Configuration)\CustomActions.CA.dll" />

    <CustomAction Id="CopyStartupConfig" Return="check" Execute="deferred" 
     BinaryKey="CustomActions.CA" DllEntry="CopyStartupConfig" />

...
</Product>

And finally, take care of the install sequence. This ensures that this is done at the very end and that the product wasn't previously installed, the second radio button was selected and that the file was actually selected. Note that the file path check is technically not needed at this stage as I do validation on my Next button which doesn't allow me to click on the Next button unless the textbox containing the config file has been filled.

<InstallExecuteSequence>
  <Custom Action="CopyStartupConfig" Before="InstallFinalize">
    <![CDATA[NOT Installed AND CONFIGURATIONSELECTION = 2 AND FILEPATH <> ""]]>
  </Custom>
</InstallExecuteSequence>

...

As for the custom action itself, it's a simple copy file:

    [CustomAction]
    public static ActionResult CopyStartupConfig(Session session)
    {
        session.Log("CopyStartupConfig");
        try
        {
            string sourceFilename = session.CustomActionData["STARTUPCONFIG"];
            string targetFilename = Path.Combine(session.CustomActionData["APPLICATIONFOLDER"], Path.GetFileName(session.CustomActionData["STARTUPCONFIG"]));

            session.Log("SourceFileName: " + sourceFilename);
            session.Log("TargetFilename: " + targetFilename);

            session.Log("About to copy " + sourceFilename + " to " + targetFilename);
            File.Copy(sourceFilename, targetFilename);
            session.Log(sourceFilename + " copied successfully.");

            return ActionResult.Success;
        }
        catch (Exception ex)
        {
            session.Log("An unhandled exception has occured in CopyStartupConfig: " + ex.ToString());
            return ActionResult.Failure;
        }
    }

Remember that this works because the condition is applied on InstallFinalize and both variables will have been set one way or the other as I'm calling my custom dialog which includes the radio buttons, file selection, etc...

The above appears to work, but I'll obviously have to spend more time testing it and I'll also have to take care of a customaction to delete the config file when uninstall occurs and I'll have to make sure that nothing happens when if repair or change is called.

UPDATE: I originally deleted my answer as I noticed too late that it wasn't actually working and I was missing a key part which was to pass parameters to my custom action. I assumed this was done automatically but this doesn't appear to be the case.

Anyway, after researching the problem further, I found this article which explained it quite clearly:

Creating Custom Action for WIX Written in Managed Code without Votive

So here is the missing piece:

You need to include the variables you want to pass to the custom action in a custom action of its own and use a semi-colon between each value:

<CustomAction Id="SetCopyStartupConfigDataValue" Return="check"
              Property="CopyStartupConfig" Value="APPLICATIONFOLDER=
              [APPLICATIONFOLDER];STARTUPCONFIG=[FILEPATH]" />

Now make sure to include this custom action in your InstallSequence and set it to be triggered before copy action is called:

<InstallExecuteSequence>
  <Custom Action="SetCopyStartupConfigDataValue" Before="CopyStartupConfig">
   NOT Installed</Custom>

  <Custom Action="CopyStartupConfig" Before="InstallFinalize">
    <![CDATA[NOT Installed AND CONFIGURATIONSELECTION = 2 AND FILEPATH <> ""]]>
  </Custom>
</InstallExecuteSequence>

The final step was to modify my custom action. I've updated in the above code, but in short, in order to access the variables you've passed, you don't want to use session["STARTUPCONFIG"] but instead, you want to use session session.CustomActionData["STARTUPCONFIG"]

Hope this helps!

Upvotes: 2

Sourav Kundu
Sourav Kundu

Reputation: 428

Have you tried robocopy? I had a requirement of copying some files over and robocopy helped.

<CustomAction Id="ConfigureApp_Cmd" Property="ConfigureApp" Execute="immediate"
    Value="&quot;robocopy&quot; &quot;[FILEPATH]&quot; &quot;[INSDIR]&quot;" />
<CustomAction Id="ConfigureApp" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/>

<InstallExecuteSequence>
    <Custom Action="ConfigureApp_Cmd" After="StartServices"><![CDATA[NOT(Installed)]]></Custom>
    <Custom Action="ConfigureApp" After="ConfigureApp_Cmd"><![CDATA[NOT(Installed)]]></Custom>
</InstallExecuteSequence>

If the destination location has to be purged and if the source directory structure has to be maintained then we may add those options

<CustomAction Id="ConfigureApp_Cmd" Property="ConfigureApp" Execute="immediate"
    Value="&quot;robocopy&quot; &quot;[FILEPATH]&quot; &quot;[INSDIR]&quot; /PURGE /e" />
<CustomAction Id="ConfigureApp" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/>

I am assuming that FILEPATH is a path that is being passed as a parameter when the installer is called with something like: msiexec /i FILEPATH= /qn /l*v install.log

Let me know if this works.

Upvotes: 1

Related Questions