Introduction
In a Visual Studio Installer (VSI) project, we can add a Customer Information dialog box in the user interface. This dialog box has an input field for a serial number. There is minimal support for validating this field in visual studio; and unfortunately there are only few articles talking about how to include custom code to validate the serial number that is entered by the user. This article provides the necessary steps to do custom validation in VSI setup projects using external
DLL file written in C++ or Delphi and I'll give two demos in both C++ and Delphi.
Create simple VSI setup project
I'll try to make things clear and simple in this article; so, we will create just an empty setup project (nothing to install) because we just want to test the serial number validation. In Visual Studio 2010 add new project, select Other Project Types then Visual Studio Installer and choose Setup Project and name it 'SetupDemo' .
Create Customer Information dialog
In the Solution Explorer panel click User Interface Editor button.
Go to the User Interface tab and select Install-Start node and from the context menu choose Add Dialog.
Choose Customer Information dialog and click Ok, then change this new dialog order to be after the Welcome dialog.
To activate the serial number validation select Customer Information dialog and from the property panel set the
ShowSerialNumber
to True.
The SerialNumberTemplate property
By default this property specifies a template used to validate serial number entry and it also determines how the text boxes will appear in the dialog box. For example, the default of this property '<###-%%%%%%%>' creates two text boxes separated by a dash surrounded by spaces. (###) simply verifies that the user has entered three digits. (%%%%%%%) is validated by an algorithm that adds the digits together and divides the sum by 7. If the remainder is 0, validation succeeds; otherwise, it fails. Of course this serial can be broken easily by entering this serial 000-0000000. If we want to make serial number harder to guess we should use more complicated
template using these special characters:
- (#) Requires a digit that will not be included in the validation algorithm.
- (%) Requires a digit that will be included in the validation algorithm.
- (?) Requires an alphanumeric character that will not be included in the validation algorithm.
- (^) Requires an uppercase or lowercase character. Numeric digits are not valid here.
- (<) Any characters to the left of this character will not be visible in the dialog box.
- (>) Any characters to the right of this character will not be visible in the dialog box. Required as a terminator if the < character is used.
You will face another problem when using more complicated template. For example, suppose we want to make serial number like the Microsoft product key (CB48T - GGBCX - H269K - C9W64 - X2RWD) this serial would be represented in the
SerialNumberTemplate
property as "^^%#^ - ^^^^^ - ^%%%^ - ^%^%% - ^%^^^", but this template would create 14 TextBoxes not 5; because each time you change the editable character a new text box is created. I think no one would like to use 14 TextBoxes to validate the serial number; so, the best way to get around all these problems is to use an external library to validate the serial number. Using this library with the help of
SerialNumberTemplate
property, then the Sky's The Limit; so, we still need the
SerialNumberTemplate
property to identify how many groups in the serial number. In this sample I will use the default template; this means serial number consists of two groups of numbers and the validation will be done inside the external library. Now build the setup project and close it.
Coding Section
Before moving to the code section; I want to mention here that for sake of simplicity I suppose user will enter 11 digits in the serial textbox; but, In real project we should add more code to check the length of the serial and the type of character if it's digit or not and so forth. The validation process depends on reading the serial textbox value. We can achieve this with help from
msi.lib; this library gives us bunch of methods to work with installer and
MSI packages (*.msi). These functions can interact with the Windows Installer runtime by using parameter of type
MSIHandle
which passes the handle of the Windows Installer session. The external library -like the validation library we are about to make- will be loaded into memory during installation so it can get the handle of the installer session and pass it to the
msi.lib functions. The second important thing in the validation process is: we will use a helper property 'PIDCHECK
' which we will add later to the msi package using Orca tool. When serial number value passes the validation we set PIDCHECK value to 'TRUE' and when the serial fails we set this property to 'FALSE'. The Serial number value exists in built-in property named 'PIDKEY'. We will use msi.lib method
MsiGetProperty
to read this property, then check/process this value and finally use
MsiSetProperty
method to save the result into the helper
property PIDCHECK
. My simple validation in this sample will be just like this: the remainder from the division of (third number + last number) by three should be Zero.
Create validation library in C++
In visual studio 2010 create new C++ Win32 dll project, name it CheckPIDDll .
We need one function to validate the serial number; so, modify the source of this library like this:
#include "stdafx.h"
#include <msi.h>
#include <msiquery.h>
#include <tchar.h>
#pragma comment(lib, "msi.lib")
UINT __stdcall VerifyPID(MSIHANDLE hInstall)
{
TCHAR szPidKey[MAX_PATH];
DWORD dwBuffer;
dwBuffer = MAX_PATH * sizeof(TCHAR);
MsiGetProperty(hInstall, TEXT("PIDKEY"), szPidKey, &dwBuffer);
int n1 = _ttoi(&szPidKey[2]);
int n2 = _ttoi(&szPidKey[10]);
int n3 = (n1 + n2) % 3;
if (n3 == 0 && n1 != n2)
MsiSetProperty(hInstall, L"PIDCHECK", L"TRUE"); else
{
MsiSetProperty(hInstall, L"PIDCHECK", L"FALSE");
MessageBox(NULL, L"serial number is not valid.",
L"Installer", MB_OK | MB_ICONINFORMATION);
}
return 0;
}
In order to export this function to the outside world; add new Module-Definition File (.def), name it CheckPIDDll and modify it like this:
LIBRARY "CheckPIDDll"
EXPORTS
VerifyPID
Build
this project, the output file CheckPIDDll.dll is now ready to be used in the
setup project.
Create validation library in Delphi
In Delphi 2010 (or
any Unicode Delphi version) add new Dll project.
There is a good Delphi wrapper for msi.lib from JEDI and you
can download it here : http://sourceforge.net/project/platformdownload.php?group_id=121894.
Now we can write Delphi DLL library that does
the same validation as the previous C++ library like this:
library CheckPIDDll;
uses
SysUtils,
Classes,
Windows,
JwaMsi,
jwaMSIQuery;
function VerifyPID(hInstall: MSIHandle): Integer; stdcall;
var
sPidKey: PChar;
dwBuffer: Cardinal;
sKey: string;
n1,n2,n3: Integer;
begin
dwBuffer := MAX_PATH;
sPidKey := strAlloc(MAX_PATH);
try
MsiGetProperty(hInstall, 'PIDKEY', sPidKey, dwBuffer);
sKey := string(sPidKey);
n1 := StrToInt(sKey[3]);
n2 := StrToInt(sKey[11]);
n3 := (n1 + n2) mod 3;
if (n3 = 0) and (n1 <> n2) then
Begin
MsiSetProperty(hInstall, 'PIDCHECK', 'TRUE');
End
else
Begin
MsiSetProperty(hInstall, 'PIDCHECK', 'FALSE');
MessageBox(0, PChar('serial number is not valid.'),
PChar('Installer'), MB_OK or MB_ICONINFORMATION);
End;
finally
StrDispose(sPidKey);
end;
result := 0;
end;
exports
VerifyPID;
begin
end.
Build this project and we are ready to go to the next step.
Orca tool
Orca is a Windows Installer package editor designed to
provide full access to the database tables that compose a Windows Installer package.
It provides powerful access to all features of the Windows Installer. This tool
provided as part of the Windows Installer SDK; so, if you didn't work with Orca
or install it before, this tool may exist in your hard drive if you have visual
studio; so, just search for 'Orca.msi' in your Program Files folder and install
the tool. You can also download it from this link http://www.microsoft.com/en-us/download/details.aspx?id=6510
. In this article I'm not going to explain Orca in details. I want just to show
you the necessary steps for adding the validation library to the msi package.
Edit the MSI package and add the validation library
Run Orca tool and open the MSI package of the setup project
demo we made it before 'SetupDemo.msi'.
Select Binary table from the left panel and from the context menu select Add
Row , name the new row 'CheckPIDDll
' and set the data value to the path of the
validation library (either Delphi or C++ version).
Select Custom Action table from left panel and add new
Custom Action name it CheckSerial, set the Type to 1 and set the Source to CheckPIDDll
and Target to VerifyPID.
Select Property table and add new property name it 'PIDCHECK' and set its default value to FALSE.
Now we need to assign the CheckSerial
custom action to Click
Event of the 'Next' button in the Customer Information dialog; so, select ControlEvent
table and in the right panel scroll down to the rows contain CustomerInfoForm
events. We are interested in the NextButton events only; so, Change the row
contains event ValidateProductID like this:
Event= DoAction, Argument=
CheckSerial
. Change the NextButton NewDialog event condition into
((PIDCHECK =
"TRUE") AND CustomerInfoForm_NextArgs <> "" AND
CustomerInfoForm_ShowSerial <> "")
.
Now save these changes and close Orca.
Go to the Setup demo SetupDemo.msi and test the new changes. You can switch
between the Delphi version and C++ version of the validation library by just changing the binary row.
Conclusion
That was a simple demo; but after good understanding of this kind of validation process I think making setup
project with serial number like the Microsoft product key will not be so hard.
References