Friday, September 20, 2019

Installer Customer Information Dialog

When creating an MSI installer using a Visual Studio Setup Project you may want the user to enter their company name and a serial number as some sort of anti-piracy measure. Installers for many commercial products use this technique and it would nice if a similar step could be inserted in the wizard sequence in a Setup Project with minimal effort.

In the User Interface Editor you can add a Customer Information dialog which prompts for a Name, Organization and Serial number. It looks like this sample:



Note that the second field 'Organization' is optional and the default is to hide it. Toggle the visibility using the ShowOrganization property in the dialog's properties.

For many years I assumed that it was impossible to retrieve and validate the values from this dialog without writing a C/C++ custom action and manually registering it in the MSI tables. There are web articles that discuss that technique, but it was too much bother for me and I just assumed the dialog was too hard to use and I ignored it.

However, this week I found an article by accident that hinted that the Serial number could be retrieved using the PIDKEY property name. This led me to a Property Reference page where some interesting User Information properties are listed at the bottom of the page. After some experiments I found that the property names USERNAME, COMPANYNAME and PIDKEY correspond to the three fields in the dialog.

Now the challenge was to retrieve the Customer Information dialog values and validate them. I also wanted to save the values after successful install in some well-known location so they can be used by the product later at runtime (this was a personal requirement).

Some Bad News

Custom action (CA) code created by Visual Studio is not of a type that runs in the UI sequence, so it's not possible to interactively validate the serial number. This is where C/C++ code would be required with special registration.

There is still a slightly clumsy way to validate the serial number once the UI sequence ends and the install sequence starts and the managed code custom actions run. If the managed CA detects a bad serial number then it can throw an Exception to cause a message box to display something helpful, then the install rolls back and is cancelled. It's a bit of a nuisance that you can unwittingly enter a bad serial, click through a few more wizard steps, start the install and then discover the Serial is wrong, but it's probably an acceptable inconvenience.

The Good News

In summary, here is how to retrieve and validate the Customer Information dialog values.

• Write a CA class derived from the Installer class (contents discussed later).

• In the Custom Actions editor, add a CA to each of the four install steps which points to the project output containing the CA class (probably your main application project).

• Set the CustomActionData for each of the CA nodes to this (wrapped for easy reading):

target="[TARGETDIR]\"
/pidkey="[PIDKEY]"
/companyname="[COMPANYNAME]"
/username="[USERNAME]"

This is the 'trick' that lets the dialog values be passed down into the CA when it runs. If you have other custom dialogs in the UI sequence, add their properties to the list.

• In the CA's Install override method do something like this skeleton:

public override void Install(IDictionary stateSaver)
{
  base.Install(stateSaver);
  string serial = Context.Parameters["PIDKEY"];
  if (serial != "314159") throw new Exception("Bad serial number");
  // You could save the parameters now
}

Enhance this raw code to be crash-resistant, then use whatever serial validation check suits your needs. The thrown message will appear in a popup and cause the install to cancel and roll back.

If the validation passes, then you could loop through the Context.Parameters collection and write the key-value pairs to a file in a well-known location. The product could read the values at runtime and, for example, it could re-validate the serial as an extra anti-piracy measure.

Serial Validation

The logic that validates the serial number can be a simple or complex as you want. A simple scenario would be to check that the serial number matches the hash of some local environmental information such as the NetBIOS machine name or the Windows Product ID (displayed in Control Panel System). This would restrict installation to specific computers or specific copies of Windows.

Remember though that if the hash algorithm code is inside the installer then someone can easily extract it and reverse engineer it.

A more complex scenario may involve "phoning home" to a web service where sophisticated licensing rules could be implemented.

Summary

So after a decade of ignoring the Custom Information dialog because I thought it was too tricky, a tiny hint led me to discover that there is not really any trick at all to using it. The 'trick' was discovering the special property names USERNAME, COMPANYNAME and PIDKEY.

Note that the first two text fields in the Customer Information dialog seem to be prefilled with the name of the current Windows user account and the Windows registered customer name respectively. If you blank the fields out and continue, the same default values seem to be used anyway.

No comments:

Post a Comment