Pass ConnectionString to Custom Action in WiX Installer (escape semicolon)
Asked Answered
P

2

1

This is my first project with WiX.
I'm creating an installer for Windows Service and during the installation, I need to collect some configuration data that the service will use. This includes the connection string to the database.

<Binary Id="CustomActionBinary" SourceFile="$(var.ServiceSetupActions.TargetDir)$(var.ServiceSetupActions.TargetName).CA.dll"/>
  <CustomAction
    Id="ServiceSetupActions"
    BinaryKey="CustomActionBinary"
    DllEntry="SaveCompanySettings"
    Execute="deferred"
    Return="check"
    Impersonate="no" />
  <CustomAction
    Id="SetupCustomProperties"
    Property="ServiceSetupActions"
    Value="DBTYPE=[DBTYPE];CONNECTIONSTRING=[CONNECTIONSTRING];INSTALLFOLDER=[INSTALLFOLDER]"/>

<InstallExecuteSequence>
  <Custom Action="SetupCustomProperties" Before="ServiceSetupActions" />
  <Custom Action="ServiceSetupActions" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>

Problem is that WiX is using a semicolon as a data separator (https://github.com/wixtoolset/Dtf/blob/09dc7b4e4494182c0906bf5492f12e09c918444f/src/WixToolset.Dtf.WindowsInstaller/customactiondata.cs#L32), so the connection string that is entered during the setup is incorrectly deserialized in my custom action.

The question is: How can I correctly pass a string that contains a semicolon to custom action (using a session.CustomActionData) so it will not be misformed.

The alternative is a full SQL connection dialog that will ask for server, database, user name, and password, but the service can handle PostgresSQL and MS SQL and sometimes the connection strings may contain some modifications.

Pyromania answered 22/2, 2021 at 18:35 Comment(3)
Afaik, you should use a separate action to setup every property valueJerri
@PavelAnikhouski this does not make sense. If I have one config file and 10 settings I want to read it once and save it once. My custom action is based on this: #46344111Pyromania
I updated my answer below, just in case you didn't get a notification. See the DTF sample please.Cess
B
0

I see you tagged this C# so I'm assuming your using DTF custom actions.

In DTF you can create another custom action and run it in immediate execution. In that action you can create a CustomActionData collection, populate the key value pairs and then call session.DoAction() and pass it the name of the deferred custom action to schedule and the CustomActionData collection. The serialization magic will happen magically and the key value pairs will be available in the deferred custom action simply by saying session.CustomActionData(key);

Barbirolli answered 22/2, 2021 at 20:39 Comment(2)
I'll try to create another action that will escape (replace a single semicolon with two semicolons) and store the connection string in another property, then I'll pass the escaped connection string to my deferred action. I hope this will work. Still learning WiX :)Pyromania
I know it seems complicated but it's designed to pass complex data. Check out my related blog article: blog.iswix.com/2011/10/beam-me-up-using-json-to-serialize.htmlBarbirolli
C
0

Please check this sample and see if it works for you: https://github.com/glytzhkof/WiXDeferredModeSample

Here is another version of the sample, this one uses the DTF CustomActionData class for more "auto-magic": https://github.com/glytzhkof/WiXDeferredModeSampleDTF - it will schedule the custom actions for you and you can just access the properties in deferred mode. Limited testing done.

  1. Set an upgrade GUID in this source line where it says "PUT-GUID-HERE" - you can create a GUID here.

  2. Change the property MYPROPERTY to contain semicolons ;. Do so at this WiX source line. You can use the below sample text if you like (anything else will do of course):

    <Property Id="MYPROPERTY" Hidden="yes" Secure="yes">Test;Test1;Test2</Property>
    
  3. Compile and do a test run. There will be a message box showing and it should contain the full string you specified in the source file.


If you want to combine several property values inside the string you send to deferred mode you have a few options. The simplest is to set several properties with the values you need and then combine them in a string sent to deferred mode. You can set the properties in several ways: dialog, command line, input boxes, etc...:

  • MYCOMPANY = "Some Company"
  • MYDATABASE = "TheDatabaseName"
  • Etc...

Then you call Session.Format in an immediate mode custom action to resolve values in your string. Something like this:

COMPANYID=[COMPANYID];DBTYPE=[DBTYPE];CONNECTIONSTRING=[CONNECTIONSTRING];INSTALLFOLDER=[INSTALLFOLDER]

var CAdata = Session.Format("MYCOMPANY=[MYCOMPANY];MYDB=[MYDB];MYDBTYPE=[MYDBTYPE]");
session["MYPROPERTYSENTTODEFERREDMODE"] = CAdata;

You also need a Type 51 CA to actually send that string to the deferred mode custom action. You see how that is done in the sample above.

Then you retrieve the string value of MYPROPERTYSENTTODEFERREDMODE in a deferred mode custom action and you should be able to use it directly?


Links:

Crystie answered 23/2, 2021 at 2:54 Comment(5)
This works for a single property, but I'm passing multiple properties like so: COMPANYID=[COMPANYID];DBTYPE=[DBTYPE];CONNECTIONSTRING=[CONNECTIONSTRING];INSTALLFOLDER=[INSTALLFOLDER]. I'll try to escape my connection string just before I pass it to deferred action.Pyromania
You don't need to use the DTF CustomActionData class, you can construct the string you pass to deferred mode yourself via string concatenation and property resolution via calls to Session.Format (resolves embedded properties in the string) - in fact that is what most people do. How do you acquire the data you need? Do you ask for the details in a custom dialog or use input boxes or set properties on the command line or what?Cess
I use input boxes. I have control with type edit. Right now I use publish event that calls custom action that is escaping my connection string (replacing the single semicolon with two semicolons) and storing in a different property. Then I'm calling my deferred action that is saving that connection string in json file. Can I do string.replace when I click on next button so I won't have to call custom action just to do that simple thing?Pyromania
I have tested the DTF CustomActionData class a bit and it looks pretty good. I will check in the sample when I get to verify it a bit more. I believe it will do what you need, it is just a bit hard to comprehend what you should and should not do. The feature is pretty auto-magic and does a lot of the work for you in terms of getting the property data to your deferred mode custom action. Then you can do simple string concatenation in that custom action - which is very easy.Cess
I just checked in the new sample, not tested that much. Give it a test run if you can: github.com/glytzhkof/WiXDeferredModeSampleDTFCess

© 2022 - 2024 — McMap. All rights reserved.