Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Delphi

Building installation project with serial number validation using VSI, Orca, C++, or Delphi

4.75/5 (9 votes)
2 Apr 2013CPOL7 min read 55.6K   1K  
Building installation project with serial number validation using VSI, Orca, C++, or Delphi.

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' . 

Image 1

Create Customer Information dialog

In the Solution Explorer panel click User Interface Editor button.

Image 2

Go to the User Interface tab and select Install-Start node and from the context menu choose Add Dialog.

Image 3

Choose Customer Information dialog and click Ok, then change this new dialog order to be after the Welcome dialog.

Image 4

To activate the serial number validation select Customer Information dialog and from the property panel set the ShowSerialNumber to True.

Image 5

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 .

Image 6

We need one function to validate the serial number; so, modify the source of this library like this:

C++
#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);       

   // Get the PIDKEY property     
   MsiGetProperty(hInstall, TEXT("PIDKEY"), szPidKey, &dwBuffer); 

   //check PIDKEY here
   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");//PIDKEY passes check
   else 
   {
      //PIDKEY doesn't pass check
      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.

Image 7

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:

Delphi
library CheckPIDDll;

uses
  SysUtils,
  Classes,
  Windows,
  JwaMsi,
  jwaMSIQuery;

{$R *.res}
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
    // Get the PIDKEY property
    MsiGetProperty(hInstall, 'PIDKEY', sPidKey, dwBuffer);
    //check PIDKEY here
    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');//PIDKEY passes check
     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).

Image 8

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.

Image 9

Select Property table and add new property name it 'PIDCHECK' and set its default value to FALSE.

Image 10

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 <> "")
.

Image 11

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   

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)