1. Validating a Product Key in a VS.NET Install
The MSDN article Q253683 describes how to validate a serial number during an MSI
install created with VSI 1.1. However, the architecture of the MSI has changed somewhat
in VS.NET and the instructions given for creating the custom action DLL may be difficult
to follow for developers who are not experienced C++ programmers.
This article outlines how to adapt the procedure to an MSI created with VS.NET, and also
gives detailed instructions on how to compile the custom action DLL.
Background
There are a wide variety of product registration strategies.
On one end of the spectrum are the typical quot;try-and-buyquot; techniques used with relatively
small Internet download applications. The simplest technique is creating two downloads,
one for the trial and one for the purchased product. More sophisticated methods usually
involve embedding the registration code within the application itself, and rely on registry
entries to determine when the trial ends or if a license has been obtained.
At the other end of the spectrum are shrink wrapped mass produced media that require a
product key (PID) such as those Microsoft employs for its products.
Neither of these cases require special packaging skills from the typical developer. In the
first case, the registration is achieved by familiar techniques - simply programming the trial
and registration code into the app itself. The latter case invariably is handled by a
department that exists solely for deployment management, and the code developers are not
part of the process.
Occasionally, the developer themselves need to create media such as CDs for distribution
without having the benefit of support from a dedicated group. This presents a problem for
the app developer who generally does not have a lot of packaging expertise. This article is
for the developer who needs to create a small number of media and wishes to require the
user to enter a product key during the setup.
A simple technique that is manageable for a small number of deployment media is to create
a product key file that uses some sort of encryption, and include it on the distribution
media. The setup can then be modified to include a custom action that reads the product
key file and validates the product key entered by the user.
This technique is outlined here.
Philosophy of licensing
2. This is not a discussion of licensing strategies, however one concept merits discussion.
Some developers spend an undue time and effort trying to ensure their product is not used
without a valid license. This is a futile endeavor.
The simple try-and-buy strategies are used to encourage most people to simply buy a
relatively inexpensive product rather than using it illegally. But anyone who has a good
working knowledge of the registry and programming techniques can figure out how to
circumvent a trial period restriction. The philosophy here is that most people do not have
this expertise, and besides, how much effort is it worth to avoid paying $39.95 for a
spyware utility?
On the other hand, there is real interest in protecting higher value products like those
marketed by Microsoft. But it is simply not feasible! Obviously, if you require a product
key, you must supply the key with the media, and there is no way to prevent people from
simply making copies of the product. Well at least not unless you want to implement an XP
type registration program!
So why do companies like MS invest the considerable resources required to create mass
media with license keys? There are several reasons. The ROI comes not from the license
key, but rather from the serial number! It is reasonable to assume that while many people
who acquire legal software never actually register it, many do register. And that provides
MS with valuable marketing information about how their products are acquired, because
each time a product is registered, MS can look at the serial number and determine if the
product was acquired as an OEM included with a new computer, a standalone shrink-
wrapped app such as MS Word that was purchased from a vendor, or a shrink-wrapped
suite of products like Office that was purchased from a vendor. All this information helps
drive how they price, package, and distribute their products!
Also, having a standard process for generation of license keys and media makes it much
simpler to prosecute bootlegged illegal products, but in practical terms this is much less of
a consideration than acquiring marketing demographics.
Realistically, requiring a user to have a valid license key using the technique described in
this article does very little to keep unlicensed copies from being made. So why bother? In
my opinion, the reason is mainly if you create media for distribution of your product, users
tend to expect a license key. Including this is mostly about creating a professional, high
quality presentation for your product deployment. But you should understand that in terms
of preventing unlicensed use of your product, the big boys have never been able to
accomplish this and neither will you! Sorry, but that is the way it is!
What You'll Need
This procedure requires you to manually edit the msi file created by VS.NET manually
using the Orca tool. Download the Microsoft Windows Installer SDK if you do not
already have Orca installed.
3. There was a time when you could just download a Windows Installer SDK from Microsoft.
However, the brain tank at Redmond, in their mysterious quot;wisdomquot;, decided to make this
unavailable. Now you have to download the 250MB Platform SDK just to get Orca???
You can still get the ancient Version 1 MSI tools, and luckily, the original version of Orca
will allow you to open the version 2.0 msi created by VS.NET. (I don't know about 2005,
but VS 2002 and VS 2003 create version 2.0 installers).
Get it here, while you still can!
Version 1 Windows Installer SDK
Then find and run the Orca install.
You will also need a C++ compiler. VS.NET C++ and Visual Studio 6 C++ are both
adequate. The article describes the procedure using VS.NET C++. The setup project and
the dll were both created with VS 2002 but should be applicable to VS 2003 as well.
About the Download
The project and the source code for creating the custom DLL in VS.NET C++ is included
in the Sample Code Download, along with a sample MSI that has been modified to perform
PID validation is also included so that you can examine the MSI of a typical modified
package in Orca.
Procedures - Creating the Custom Action DLL
First we'll create the Custom Action DLL.
1) Create the Custom Action Dll Project
Start Visual Studio NET.
Create a new VC++ Project. Select Win32 Project as the type, and name the project
PIDCheckDll.
The Win32 Application Wizard will start. Select the Application Settings option. Select
the following project settings:
Application Type: Dll
Additional Options: Empty Project
Click the Finish button to create the project.
2) Configure the Dll Project
4. From the IDE main menu select Build, Configuration Manager. The Config Manager dialog
will be displayed. Set Active Solution Configuration to Release and click Close.
Locate the include folder where you installed the MS Platform SDK, typically C:Program
FilesMicrosoft SDKinclude.
Select Project, Properties and the Properties dialog will be displayed. Verify that the active
configuration is Release. In the Configuration tree select CC++ > General. In the listview
select Additional Include Directories and click the browse button. The Additional Include
Directories dialog will appear. Click the New Folder button, and a folder dialog will
appear. Browse to the Microsoft SDKinclude folder. Click Open to select the folder.
Click OK to dismiss the Additional Include Directories dialog.
The Properties page should now look like the figure below.
Next, in the Configuration tree select Linker > Input. In the listview select Additional
Dependencies and type msi.lib.
The Properties page should now look like the figure below.
5. Click Apply, then OK to close the Properties dialog. The project is now configured
properly.
About the Code
This is not a C++ tutorial, so I will only briefly describe what the code does.
The dll exports a custom validation function, VerifyPID, which the setup will call to validate
the serial number (PID) entered during the setup.
VerifyPID calls a MSI API function to determine the location where the MSI is running.
This value is typically needed to locate a subfolder on the install media where you have
placed an encrypted file that contains the specific PID that unlocks the install. Since we
can determine the run location during the setup, you do not need to worry about whether
the CD is in D or E drive, etc. You simply find the key file relative to the main msi path
and perform your de-encryption algorithm to return the specific PID key value required by
the setup.
VerifyPID next calls the MSI API to obtain the value the user entered for the PID. The PID
entered is compared to the PID extracted from the encrypted key file included on your
6. media. The VerifyPID then sets the PIDCHECK property in the msi to either true or false
depending on the comparison results.
The format for your PID can be virtually anything. We will see how to control the fields
displayed by the setup later. The important thing to keep in mind is that the PID format
configured in the setup must match your actual PID string exactly, including any spaces
and dashes. The comparison succeeds only if the two strings match exactly, character for
character.
For sake of example, the code shown here simply returns a string literal, and does not use a
key file or encryption. You can easily modify the code shown to add this functionality.
The sample code also includes minimal error checking for simplicity.
3) Add the Code to the Dll Project
Select Project, Add New Item. In the dialog select C++ file and name the file PIDCheck.
Click Open.
Add the code in the sample PIDCheck.cpp file included in this download to the cpp file just
created.
Save all, and then build the solution. The compiled dll will be created in the Release
subfolder of the project.
Procedures - Configure the MSI To Validate a PID with the Custom
Action
All modifications will be performed manually using Orca after the .msi is built. I assume
that you already know how to build an msi setup using Visual Studio NET. Create your
setup in the usual fashion. Make sure you add the Customer Information dialog to the user
interface sequence. Finally, build the release version of your setup.
Adding PID validation to an msi requires the use of Microsoft's msi editing tool Orca.
First we will add the custom dll to the msi package. The dll is embedded in the msi file
and does not need to be distributed separately. Next we will configure a custom action that
instructs the msi on how to call the dll functions when the setup is run. We will configure
the property values required by the custom action to retrieve the PID value and return a
boolean result indicating the validation outcome. Finally, we will add the statements that
control the setup dialog behavior for the dialog that collects the PID input from the user.
1) Add the Dll to the MSI
Launch Orca.exe, and open the release version of your setup .msi file. Orca is a database
editor. Tables are displayed in the list on the left, and fields in a selected table are
displayed in the listview on the right.
7. Select the Binary table. Double click on an empty row, or select Tables, Add Row from
the Orca menu, and a dialog will be displayed. In the Name field enter PIDCheckDll. Click
on the Data field, and a browse button will appear. Browse to the compiled dll file and
select it. Click OK and the dll will be embedded in the msi as a binary stream.
2) Define the Custom Action
Now select the CustomAction table. Add a new row, and enter the following values in the
dialog.
Action: CheckPID
Type: 1
Source: PIDCheckDll
Target: _VerifyPID@4
The Action field identifies the custom action. The Type field is set to integer 1 which
instructs the Windows Installer that the custom action uses a dll embedded as a binary
field. The Source field identifies the binary stream (which we included in the first step).
The Target field instructs the Windows Installer how to call the dll - in this case
_VerifyPID@4 is the C style target function call exported by the compiled dll.
2) Configure Property Values
Next we need to configure the property values required by the PID validation. First we
will add a new property - PIDCHECK - that the dll will set to TRUE or FALSE based on
the validation outcome.
Select the Property table. Add a new row, and enter the following values in the dialog.
Property: PIDCHECK
Value: FALSE
This creates the custom property required by the dll and initializes it to a starting value of
FALSE. If the validation succeeds, the dll will set the property value to TRUE which will
be used as a condition to continue the installation.
Now we will makes sure the PID entry fields are formatted correctly by setting the
PIDTemplate property. The Windows Installer automatically configures text input boxes in
the CustomerInfo dialog to conform to the template specified. See the SDK docs for more
details on how to format input templates - it works essentially as a masked textbox, and the
dialog will enforce field level key validation based on the mask. In this example we have
specified a PID value of 123 - 4567890. The corresponding template is <### - #######>.
8. Select the existing PIDTemplate property and change the value is to:
<### - #######>
When the setup is run you will see that the dialog appears with two input boxes, separated
by a space, dash, space. The first box will accept three numeric digits, the second will
accept seven numeric digits.
Next we need to enable the serial number input. By default, setups built with VS.NET
disable this field. Find the property named CustomerInfoForm_ShowSerial and you will
notice the value is set to 0 (false0. Change this property value to 1 (true) to enable the
mask input boxes.
The last piece of the puzzle is the PIDKEY property that the dll retrieves to determine what
the user has entered for the PID value. This is one of the many intrinsic properties (like
TARGETDIR) supplied by the Windows Installer. We do not need to add this property, it
always exists, is always initialized to null, and is set automatically to the string entered by
the user. The setup will take care of concatenating input values that span multiple text box
fields, etc.
3) Configure Dialog Controls
At this point, we have the basic elements in place for the validation. We've added our dll
that performs the string validation, instructed the Windows Installer how to call the dll, and
configured the input and return properties required to allow the dll to read the user input
and return a boolean outcome.
First we have to enable the serial number controls.
Open the Control table. Find the series of rows for the CustomerInfoForm dialog. Each
represents a control on the PID entry dialog. Find the row entry for Control named
SerialEdit, and set the Attribute column value to 3 to enable this control. Repeat this
operation for the controls named SerialLabel, and SerialBodyText.
4) Configure Dialog Conditions
The last step is to configure the dialog to call the dll validation function at the correct time
in the install, and configure the dialog controls to either prevent or allow the user to
continue the installation base don the PID validation outcome.
This is the most confusing part of the process. Like everything else in an msi, instructions
are in the form of database entries. The Windows Installer reads table values and creates
an installation script accordingly. Dialog actions are no different, and we have a
ControlEvents table that instructs the installation how to respond to user interface events
like button clicks. Although the format is a little different, the basic concept is not different
from programming a conventional GUI.
9. In this case, the actions we need to take are simple and intuitive. When the user clicks the
Next button on the dialog that accepts the PID, we need to call our custom validation
function. We also need to either prevent or allow navigation to the next dialog triggered
by the Next button click based on the outcome of the validation. The main difference when
working with an msi is that instead of writing the appropriate code in a single button click
event handler, we have to work with a database format.
Select the ControlEvent table in Orca. You will see numerous entries - for each dialog and
each control on each dialog. Find the section where the Dialog field is CustomerInfoForm.
These are the control events entries for the PID dialog.
Take care to make sure you enter the following changes exactly as shown, to the exact
table row described or the sequence will be corrupted!
Find the row where the Control value is NextButton and the Event value is
ValidateProductID.
Change the value of the Event column from ValidateProductID to DoAction. Change the
value of the Argument column from {} to CheckPID. This instructs the installation to run
our dll custom validation when the user clicks the Next button on the PID dialog.
Next, find the row where the Control value is NextButton and the Event value is
NewDialog. This controls navigation. Delete the existing entry in the Condition column.
Then enter the new conditional arguments that use our PIDCHECK property:
(PIDCHECK=quot;TRUEquot;) AND CustomerInfoForm_NextArgs<>quot;quot; AND
CustomerInfoForm_ShowSerial<>quot;quot;
This basically requires our PIDCHECK value to be TRUE (the other arguments are used
internally by the msi). If PIDCHECK is FALSE, the validatiuon failed and the user will
not be able to advance to the next dialog, effectively halting the installation sequence.
5) Finishing Up
Now save the msi. You must also close it in Orca before it will run. Launch the setup and
verify that it works as advertised.
Comments
If you've followed this process correctly, by now you have probably realized something.
MSI is an open platform. Anyone who has an editor tool like Orca and the knowledge can
open your MSI, see where you require conditions to continue the install, and edit your
setup to circumvent the validation. But then again, there is not much to stop anyone who
has a licensed copy of your software from giving the setup and the license key to anyone
else. As I stated before, licensing is about deterrents and mostly about maintaining a
10. professional looking software distribution system. People who really want to will be able
to circumvent any licensing system. That's life on the mean streets!
This page was last modified on: October 25, 2005
This page was originally created on: October 25, 2005
All Content Copyright 2005 Robert Graham All Rights Reserved.