Embedding InterBase 6.0 in 10 easy steps
This is the technical paper from a talk given at the 10th Annual Inprise & Borland Developer's Conference
By Marco Romanini - InterBase Software Corporation
Marco Romanini is a senior software engineer for InterBase Software Corporation, a subsidiary of Inprise Corporation. Marco has worked for Inprise Corporation since January 1994, in Technical Support for Paradox and Delphi. In August 1995, he joined InterBase Software Corporation in Quality Assurance, and later in Research and Development. Marco has been referred to as the "jack of all trades" at InterBase. He has worked on a wide variety of projects, including design of the install API, maintenance of UDF and Blob filter mechanisms, Webmaster for the company's intranet Web server, contributing to a company-wide effort to improve development methodology, and being the licensing guru. Marco received both his Bachelor's and Master's degrees in computer science from California State University at Chico.
Introduction
The process of installing and licensing InterBase Server or Client on a
workstation used to be a complicated one. However, InterBase 6.0
introduces the Install API and the License API, which will perform all
the complicated tasks, while allowing full flexibility of customization
of the install. While these APIs are discussed together, each is
a distinct set of functions which can be used independently of the other
if the need arises.
Install API
The Install API is a set of functions designed to perform the install,
and uninstall process. These function were designed with the intent
of allowing a programmer the maximum flexibility, while encapsulating the
tedious details of the install process. The functions provided with
this API are:
-
isc_install_set_option: Allows you to set an install option.
Options represent the components to be installed.
-
isc_install_unset_option: Allows you to unset an install option.
-
isc_install_clear_options: Clears all install options
-
isc_install_precheck: Performs some pre-install checks on the system
such as validating the source and destination directory, ensuring that
there is no InterBase server running on the machine, and checking user
permissions.
-
isc_install_execute: Performs the install according to the options
set and the directories specified.
-
isc_uninstall_precheck: Performs some pre-uninstall checks on the
system such as ensuring that there is no InterBase server running on the
machine, and checking user permissions.
-
isc_uninstall_execute: Performs the uninstall according to the information
in the uninstall file.
-
isc_install_get_message: Returns the error or warning message associated
with a message code.
-
isc_install_load_external_text: Loads a message file other than
the default one, and instructs the install API to use it. This is
intended for internationalization purposes.
isc_install_get_info: Returns the suggested destination directory,
or information about a specific option, such as the name, description,
and size.
For your convenience, the complete beta documentation of the Install
API has been included here in PDF format.
License API
The License API is a set of functions designed to give programmatic access
to process InterBase Software Activation Certificates. These are
most useful when writing an install program which uses the Install API,
however, their use is in no way limited to that. The functions provided
with this API are:
-
isc_license_check: Checks if a software activation certificate
is valid.
-
isc_license_add: Adds a software activation certificate to the InterBase
license.
-
isc_license_remote: Removes a software activation certificate from
the InterBase license.
-
isc_license_display: Returns the list of software activation certificates
which are in the InterBase license.
-
isc_license_get_msg: Returns a message associated with an error
code.
For your convenience, the complete beta documentation of the License
API has been included here in PDF format.
Note: If using the License API it is extremely important that the library
and entry points be dynamically loaded only once the install is complete.
This ensures that the finished environment after install is used to add
the license, rather than the one before. If you are using Delphi,
we have provided a unit containing functions which automatically handle
the dynamic loading of this library.
The License API does not provide a facility for installing custom license
files. These are license files created for VARs for specific needs,
and need only be concatenated to the end an existing license file if present.
A sample way of doing this is demonstrated in the demo install program
provided in the AddLicenseInformationClick method.
Installing InterBase
You can use the Install API to write a stand alone install for InterBase,
or embed the InterBase install into a larger install program, usually the
install for a custom application. Traditionally you would have called
WinExec to launch our setup.exe and shown your customers that they were
installing InterBase as part of your product, or you would have embedded
a complete install program for InterBase, which you had to create, following
the Embedded Installation Guide. With InterBase 6.0, you can
embed the InterBase install into any program by following these ten easy
steps:
-
Perform install precheck
-
Check software activation certificate
-
Get suggested installation directory
-
Get option information
-
Set install options
-
Perform install
-
Add software activation certificate and/or license file
-
Create start menu entries
-
Start the InterBase Guardian
-
Register Uninstall Program
Step 1: Perform install
precheck
Perform a precheck by calling isc_install_precheck at the beginning
of the install to ensure that it is allowable to install InterBase.
If we set the INTERBASE option prior to doing the precheck, we are suggesting
that we might install the complete product. This will maximize the
precheck to include: Search for incompatible versions od InterBase such
as InterBase 4.1 for NT (Classic), checking that version of InterBase Server
4.2 or later is not currently running, checking the Operating System version
(Windows NTtm 3.51 is not supported), and
checking user permissions. By doing this at the beginning the user
finds out immediately that the install can not be performed rather than
waiting until all the questions have been answered. A sample way
of performing the initial precheck using Delphi is demonstrated in the
demo install program in the PerformPrecheckBtnClick
method.
Step 2: Check
software activation certificate
If the install program prompts the user to enter a software activation
certificate, then it is a good idea to check what they have entered is
valid using isc_license_check. This would prevent them from continuing
with the install until a valid certificate is not given. This step
is optional, and most likely omitted if the user will license InterBase
directly from Inprise Corporation once it is installed, if this setup will
always use a redistributable certificate or if it will use a custom license
file. A sample way of checking the software activation certificate
using Delphi is demonstrated in the demo install program in the
CheckLicenseBtnClick
method.
Step 3: Get
suggested installation directory
Ask the Install API for the suggested installation directory by using isc_install_get_info,
requesting isc_install_info_destination. The suggested destination
directory might vary depending on any previous version of InterBase installed,
the Operating Systems being used, and the way that Operating System is
configured. It is strongly recommended that you install to the suggested
installation directory. A sample way of getting the suggested installation
directory using Delphi is demonstrated in the demo install program in the
GetSuggestedDestinationBtnClickmethod.
Step 4: Get Option Information
If the install program allows users to select which InterBase options will
be installed, it can obtain the name of the option, the disk space required
for that option, and a description of the option by using isc_install_get_info.
This step is optional. A sample way of getting the option information
using Delphi is demonstrated in the demo install program in the
GetOptionInformationBtnClick method.
Step 5: Set Install Options
Set the options to indicate which InterBase components you wish to install
by using isc_install_set_option. These options are used by
the install API to determine which files to copy and which registry entries
to create. If step 4 was performed, then this step is done by determining
which options the user has selected, otherwise, the options set might be
determined ahead of time and hard coded in the install program. A
sample way of setting the install options based on user input using Delphi
is demonstrated in the demo install program in the
SetOptionsBtnClick method.
Step 6: Perform Install
To perform the install it is suggested that you first call isc_install_precheck
one more time now that the actual install options have been set.
This is an optional part, but it will inform you of any conflicts or unsatisfied
dependencies amongst the options set. If step 4 was performed it
is strongly suggested that this final precheck be done. Once this
is done, call isc_install_execute to perform the install.
A sample way of calling isc_install_execute using Delphi is demonstrated
in the demo install program in the
PerformInstallBtnClick method.
If the install program is to display installation status, you should
write a status handling function, and pass its function pointer to isc_install_execute,
this is optional, and its need is up to the discretion of the developer.
Additionally, if the install program needs to be aware of errors during
the execution of the install, so that it can choose to rectify or ignore
the error, or abort the install, you should write an error handling function,
and pass its function pointer to isc_install_execute. The
error handling function can be written to automatically handle errors,
or to display errors to the user, and let them decide weather to ignore,
retry, or abort. If no error handling function is provided isc_install_execute
will abort immediately once an error is encountered, and return the error
number. Please see the demo install program for an example of a status
handling function DisplayStatus and an error
handling function DisplayError.
Step
7: Add software activation certificate and/or license file
To activate the InterBase software once it is installed, you can do one
of two things. The first method, available only to members of the
VAR program, is to obtain custom license files which will activate any
set of features desired, or enable any number of simultaneous users.
In this case you can concatenate these custom files with the license file
already on the computer if one is present. A sample way of doing
this using Delphi is demonstrated in the demo install program in the
AddLicenseInformationClick
method.
The second method is to add software activation certificates using isc_license_add.
Software activation certificates are made up of two numbers, and ID and
a key, and represent a predetermined set of features or simultaneous users
to activate. Any number of software activation certificates can be
combined to activate more features, as long as they are unique, that is
you can not use the same certificate twice.
The second method is just as effective as the first, except that it
provides less flexibility, and you must request that your users enter multiple
software activation certificates, or hardcode them in your installation
program. Furthermore, if you wish ot have the ability to have the
InterBase software stop working on a given date, you can only do this with
the use of license files, since they can be created to have an expiration
date, whereas software activation certificates can not.
Step 8: Create Start Menu
Entries
The Install API will ensure that the InterBase software components you
requested are correctly installed, however it is up to your install program
to create start menu entries for the InterBase software if desired.
This step is optional, and the name of the start menu entries, and folder
is up to you. A sample way of creating start menu entries using Delphi
is demonstrated in the demo install program in the
CreateShortcutsBtnClick method.
Step 9: Start the InterBase
Guardian
The Install API will ensure that the InterBase Guardian is to be started
as a service on Windows NTtm, set to start
automatically, or as an application on WIndows 95tm
and WIndows 98tm, set to start automatically.
However, if you wish the guardian to start once the install program has
finished, without making the user reboot the machine, you will have to
start the guardian. This is done by communicating with the Windows
Service Manager on WIndows NTtm, or by
calling CreateProcess on WIndows 95tm
and Windows 98tm. A sample way of
starting the InterBase Guardian using Delphi is demonstrated in the demo
install program in the StartGuardianBtnClick
method. This step is optional.
Step 10: Register Uninstall
Program
If you write an uninstall program and would like it to be present in the
list of uninstall programs in the Add/Remove Programs section of Control
Panel, you will have to register your uninstall program in the registry.
A sample way of doing this using Delphi is demonstrated in the demo install
program in the RegisterUninstallBtnClick method.
Uninstalling InterBase
As with Installing, you can use the Install API to write a standalone uninstall
program for InterBase, of embed the uninstallation of InterBase in some
lager uninstall program. The Install API creates an uninstall file
when isc_install_execute is called, the name of which is placed
in the last parameter. With this information InterBase can be uninstalled
in three easy steps:
-
Perform Uninstall
-
Unregister Uninstall Program
-
Remove Start Menu Entries
Step 1: Perform Uninstall
To perform the uninstall process call isc_uninstall_execute passing
in the name of the uninstall file. A sample way of calling
isc_uninstall_execute
using Delphi is demonstrated in the demo install program in the
PerformUninstallBtnClick method.
As with step 6: Perform Install on the installation, if the uninstall
program is to display uninstallation status, you should write a status
handling function, and pass its function pointer to isc_uninstall_execute,
this is optional, and its need is up to the discretion of the developer.
Additionally, if the uninstall program needs to be aware of errors during
the execution of the uninstall, so that it can choose to rectify or ignore
the error, or abort the uninstall, you should write an error handling function,
and pass its function pointer to isc_uninstall_execute. The
error handling function can be written to automatically handle errors,
or to display errors to the user, and let them decide weather to ignore,
retry, or abort. If no error handling function is provided isc_uninstall_execute
will abort immediately once an error is encountered, and return the error
number. Please see the demo uninstall program for an example of a
status handling function DisplayStatus and an
error handling function DisplayError.
Step 2: Unregister Uninstall
Program
Once the uninstall completed successfully, you should remove the uninstall
entry from the Add/Remove Programs section of Control Panel. A sample
way of doing this using Delphi is demonstrated in the demo uninstall program
in the RemoveUninstallEntryBtnClick
method. This step is optional.
Step 3: Remove Start Menu
Entries
The Install API will cleanup everything it did when isc_install_execute
was called, however since your install program created the start menu entries,
your uninstall program must remove them. A sample way of doing this
using Delphi is demonstartated in the demo uninstall program in the
RemoveStartMenuShortcutsBtnClick
method. This step is optional.
Summary
From now you will be able to write custom install/uninstall programs to
install both your application, as well as any components of InterBase,
with the use of the Install API, without needing to perform two separate
installs. You will also be able to activate the InterBase software
with the use of the License API.
Appendix
A: Demo Install Program Source
unit Install_main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls, checklst, Gauges, IBInstall, ShlObj, ActiveX, ComObj,
Registry, Winsvc, iblicense;
type
TInteractiveInstallDemoFrm = class(TForm)
InstallPageControl: TPageControl;
InstallPage1: TTabSheet;
InstallPage2: TTabSheet;
InstallPage3: TTabSheet;
Step1GrpBx: TGroupBox;
PerformPrecheckBtn: TButton;
Step2GrpBx: TGroupBox;
CertIDLbl: TLabel;
CertIDFld: TEdit;
CertKeyLbl: TLabel;
CertKeyFld: TEdit;
CheckLicenseBtn: TButton;
Step3GrpBx: TGroupBox;
GetSuggestedDestinatoinBtn: TButton;
DestinationDirLbl: TLabel;
DestinationDirFld: TEdit;
Step4GrpBx: TGroupBox;
GetOptionInformationBtn: TButton;
Step6GrpBx: TGroupBox;
PerformInstallBtn: TButton;
InstallProgressGauge: TGauge;
Step7GrpBx: TGroupBox;
AddLicenseInformation: TButton;
Step8GrpBx: TGroupBox;
CreateShorcutsBtn: TButton;
Step9GrpBx: TGroupBox;
StartGuardianBtn: TButton;
Step10GrpBx: TGroupBox;
UninstallFilenameFld: TEdit;
RegisterUninstallBtn: TButton;
Step5GrpBx: TGroupBox;
ComponentsListBox: TCheckListBox;
SetOptionsBtn: TButton;
procedure PerformPrecheckBtnClick(Sender: TObject);
procedure CheckLicenseBtnClick(Sender: TObject);
procedure GetSuggestedDestinatoinBtnClick(Sender: TObject);
procedure GetOptionInformationBtnClick(Sender: TObject);
procedure SetOptionsBtnClick(Sender: TObject);
procedure PerformInstallBtnClick(Sender: TObject);
procedure AddLicenseInformationClick(Sender: TObject);
procedure CreateShorcutsBtnClick(Sender: TObject);
procedure StartGuardianBtnClick(Sender: TObject);
procedure RegisterUninstallBtnClick(Sender: TObject);
private
{ Private declarations }
OptionsHandle: OPTIONS_HANDLE;
SourceDir: String;
RebootMessage: Boolean;
Cancelling: Boolean;
StartServer: Boolean;
function DisplayErrorMessage(handle: OPTIONS_HANDLE; error: MSG_NO): word;
public
{ Public declarations }
end;
function DisplayError(msg: MSG_NO; data: pointer; error_msg: TEXT): integer; export; stdcall;
function DisplayStatus(status: integer; data: pointer; const status_msg: TEXT): integer; export; stdcall;
var
InteractiveInstallDemoFrm: TInteractiveInstallDemoFrm;
const
MaxComp = 12;
ComponentOptionNumber: array [0..MaxComp-1] of OPT =
(IB_SERVER, IB_CLIENT, IB_DOC, IB_CMD_TOOLS, IB_GUI_TOOLS,
IB_DEV, IB_EXAMPLE_API, IB_EXAMPLE_DB, IB_CONNECTIVITY_SERVER,
IB_ODBC_CLIENT, IB_JDBC_CLIENT, IB_OLEDB_CLIENT);
implementation
{$R *.DFM}
{*************************************}
{ Ten easy steps to embedd InterBase }
{*************************************}
{ TODO 1: Perform Install PreCheck }
procedure TInteractiveInstallDemoFrm.PerformPrecheckBtnClick(
Sender: TObject);
var
err_no: MSG_NO;
begin
{ Perform a precheck at the begenning of the setup to ensure
{ that it is OK to install. Since we are not passing in any
{ options, all that will be done is a search for incompatible
{ versions od InterBase such as InterBase 4.1 for NT (Classic)
{ and a check of the Operating system (NT 3.51 is not supported.
{ By doing this at the begenning the user finds out immediately
{ that the install can not be performed rather than waiting until
{ all the questions have been answered.}
OptionsHandle := 0;
SourceDir := ExtractFilePath(ParamStr(0));
{ By setting all options, we guarantee the maximum precheck.
{ If we had not set INTEERBASE, but rather had used IB_CLIENT
{ We would still be checking for CLassic server instaled, but
{ we would NOT be checking for a started Super server. }
isc_install_set_option(@OptionsHandle,INTERBASE);
err_no := isc_install_precheck(OptionsHandle, PChar(SourceDir), nil);
{ Since we are not passing sufficent information to the precheck
{ we know that we will get warnings. However in this particular
{ instance we are only interested in errors, so we will only check for
{ positive return values. }
if (err_no > 0) then
DisplayErrorMessage(OptionsHandle, err_no)
else
MessageDlg('Precheck OK!',MtInformation,[mbok],0);
end;
{ TODO 2: Check License Information (optional) }
procedure TInteractiveInstallDemoFrm.CheckLicenseBtnClick(Sender: TObject);
var
retval: integer;
begin
{ Call IscLicenseCheck to validate the ID/Key. This allows you to
{ see if the id/key is valid and issue any errors immediately. This
{ step is optional, since you might not want to surface this to the
{ users, and either use a hard-coded id/key, or use a custom license
{ file. In either of these cases you would know that it is valid,
{ and therefore there is no need to check. }
retval := IscLicenseCheck(ExtractFilePath(ParamStr(0))+'biniblicense.dll',CertIDFld.Text,CertKeyfld.Text);
if ((not (retval = isc_license_success)) and (not (retval = isc_license_msg_dupid))) then
begin
MessageDlg('Could not license product' ,mtError,[mbOk],0);
end
else
MessageDlg('License OK!',MtInformation,[mbok],0);
end;
{ TODO 3: Get Suggested Destination Directory }
procedure TInteractiveInstallDemoFrm.GetSuggestedDestinatoinBtnClick(
Sender: TObject);
var
err_no: MSG_NO;
TmpDestDir: PChar;
begin
TmpDestDir := StrAlloc(ISC_INSTALL_MAX_PATH);
{ Call isc_install_get_info asking for the info_destination.
{ This will check for a preinstalled version and if one is found
{ will suggest that directory as the destination. Otherwise it
{ will use the default directory. }
err_no := isc_install_get_info(isc_install_info_destination, 0,
TmpDestDir, ISC_INSTALL_MAX_PATH);
if(err_no = isc_install_success) then
DestinationDirFld.Text := String(TmpDestDir)
else
InteractiveInstallDemoFrm.DisplayErrorMessage(OptionsHandle, err_no);
{ Don't forget to free the memory }
StrDispose(TmpDestDir);
end;
{ TODO 4: Get Option Information (optional) }
procedure TInteractiveInstallDemoFrm.GetOptionInformationBtnClick(
Sender: TObject);
var
err_no: MSG_NO;
TmpCompSize: Longint;
TmpCompName: PChar;
CompCount: integer;
begin
TmpCompName := StrAlloc(ISC_INSTALL_MAX_MESSAGE_LEN);
TmpCompSize := 0;
ComponentsListBox.Items.Text := '';
for CompCount := 0 to MaxComp - 1 do
begin
{ For every option you wish to use, call isc_install_get_info to get the diskspace
{ needed for the option}
err_no := isc_install_get_info(isc_install_info_opspace, ComponentOptionNumber[CompCount],
Pointer(@TmpCompSize), Sizeof(Longint));
if (err_no >< isc_install_success) then
DisplayErrorMessage(OptionsHandle, err_no);
{ Call isc_install_get_info again to get the names of the options. For example, IB_DOC
{ has a name of "On-line Documentation". Optionally, you could additionally call
{ isc_install_get_info with isc_install_info_opdescription to get the long description
{ for the option. This is more useful for fully interactive install to allow the
{ user to request more info on any one select option.}
err_no := isc_install_get_info(isc_install_info_opname, ComponentOptionNumber[CompCount],
Pointer(TmpCompName), ISC_INSTALL_MAX_MESSAGE_LEN);
if (err_no >< isc_install_success) then
DisplayErrorMessage(OptionsHandle, err_no);
ComponentsListBox.Items.Add(String(TmpCompName)+' ('+IntToStr(TmpCompSize)+' K)');
end;
{ Don't forget to free memory }
StrDispose(TmpCompName);
end;
{ TODO 5: Set Install Options Selected }
procedure TInteractiveInstallDemoFrm.SetOptionsBtnClick(Sender: TObject);
var
i: Integer;
err_no: MSG_NO;
begin
isc_install_clear_options(@OptionsHandle);
StartServer := False;
for i := 0 to MaxComp - 1 do
begin
if (ComponentsListBox.Checked[i]) then
begin
{ If the server option has been selected, then make a note of it so that
{ you know that you should start it at the end. }
if (ComponentOptionNumber[i] = IB_SERVER) then
StartServer := True;
{ For every checked element in the list, set the equivalent option. }
err_no := isc_install_set_option(@OptionsHandle, ComponentOptionNumber[i]);
if (err_no >< 0) then
DisplayErrorMessage(OptionsHandle, err_no);
end;
end;
MessageDlg('Options Set!',MtInformation,[mbok],0);
end;
{ TODO 6: Perform Install as requested }
procedure TInteractiveInstallDemoFrm.PerformInstallBtnClick(Sender: TObject);
var
err_no: MSG_NO;
ret_val: word;
UninstallFilename: PChar;
begin
UninstallFilename := StrAlloc(256);
ret_val := mrIgnore;
{ Call precheck one more time to make sure that the options you have selected do not
{ have any unsattisfied dependencies. If they do this is a warning, and thus can
{ be ignored if desired. }
err_no := isc_install_precheck(OptionsHandle,PChar(SourceDir),PChar(DestinationDirFld.Text));
if (err_no >< 0) then
ret_val := DisplayErrorMessage(OptionsHandle, err_no);
if (ret_val = mrIgnore) then
begin
{ Perform the install, and set the error handling call back, the status handling call back
{ and the uninstall file name. }
err_no := isc_install_execute(OptionsHandle,PChar(SourceDir),PChar(DestinationDirFld.Text),
@DisplayStatus,nil,@DisplayError,nil,UninstallFileName);
{ If an error occured and was handled by the error handling call back, and the
{ user chose to abort/cancel, then exit the install. }
if err_no = isc_install_user_aborted then
exit;
{ If an error occured at the end, and was returned by the install, display it. }
if (err_no >< 0) then
begin
ret_val := DisplayErrorMessage(OptionsHandle, err_no);
end;
{ If the user chose to abort/cancel, then exit the install. }
if not (ret_val = mrIgnore) then
begin
exit;
end;
end;
UninstallFilenameFld.Text := UninstallFilename;
StrDispose(UninstallFilename);
{ We have to manually copy our own uninstall program, since the
{ API knows nothing about it.}
CopyFile(pChar(SourceDir+'Interactive_Uninstall_Demo.exe'),
pchar(DestinationDirFld.Text+'Interactive_Uninstall_Demo.exe'),False);
{If some files could not be copied, then they will be replaced the
{ next time you re-boot. Therefore we need to inform the user of this. }
if (RebootMessage) then
MessageDlg('You must reboot in order for install to finish!',MtInformation,[mbok],0)
else
MessageDlg('Install Successful!',MtInformation,[mbok],0);
end;
{ TODO 7: Add License Information (optional) }
procedure TInteractiveInstallDemoFrm.AddLicenseInformationClick(Sender: TObject);
var
retval: integer;
DestinationLicenseFile: TextFile;
DestinationLicenseLine: String;
SourceLicenseFile: TextFile;
SourceLicenseLine: String;
SourceLicenseLineAlreadyInDestination: boolean;
const
LICENTOOL_SUCCESS = 29;
begin
SourceLicenseLineAlreadyInDestination := false;
{ Open the destination licenes file, and attempt to reset it.
{ If you get any kind of exception, then the file MUST not exist,
{ so go ahead and reWrite it, so as to create it. We know that
{ the only case in which there will be one already there, is the
{ case in which we just installed over a pre-existing version of
{ interbase. In this case the license which was there may or
{ may not apply to this version, but we must never remove a users
{ license since it belongs to them. }
AssignFile(DestinationLicenseFile, DestinationDirFld.Text+'ib_license.dat');
try
Reset(DestinationLicenseFile);
except on E:Exception do
ReWrite(DestinationLicenseFile);
end;
{ We know that there is a license file on the install media, so
{ we do not have to check for existance. Simply reset its file
{ poiner to the begining. }
AssignFile(SourceLicenseFile, SourceDir+'ib_license.dat');
Reset(SourceLicenseFile);
while not eof(SourceLicenseFile) do
begin
{ As long as there is information in the Source license file,
{ this is the one on the install media, then read one line at
{ the time, and compare it to each an evry line in the
{ destination license file. }
Readln(SourceLicenseFile,SourceLicenseLine);
Reset(DestinationLicenseFile);
while ((not eof(DestinationLicenseFile)) and (not SourceLicenseLineAlreadyInDestination)) do
begin
Readln(DestinationLicenseFile,DestinationLicenseLine);
if (SourceLicenseLine = DestinationLicenseLine) then
SourceLicenseLineAlreadyInDestination := true;
end;
{ If the source license line is not already in the destination
{ license file, then we go ahead and append it. }
if not (SourceLicenseLineAlreadyInDestination) then
begin
Append(DestinationLicenseFile);
Writeln(DestinationLicenseFile, SourceLicenseLine);
end;
end;
CloseFile(DestinationLicenseFile);
CloseFile(SourceLicenseFile);
{ Now that we have added the contents of the source license file to
{ the destination license file, we need to add the desired certificates
{ to the destination license file. To do this correctly we call
{ IscLicenseAdd to actualy add the certificate. If there
{ was a need to add multiple certificates, you could call this function
{ as many times as necessary. }
retval := IscLicenseAdd(DestinationDirFld.text+'biniblicense.dll',CertIDFld.Text,CertKeyfld.Text);
if ((not (retval = isc_license_success)) and (not (retval = isc_license_msg_dupid))) then
begin
MessageDlg('Could not license product' ,mtError,[mbOk],0);
end
else
MessageDlg('License added Successfully',MtInformation,[mbok],0);
end;
{ TODO 8: Create Start Menu entries (optional) }
procedure TInteractiveInstallDemoFrm.CreateShorcutsBtnClick(Sender: TObject);
const
ShortcutNames = 'License Registration Tool'+#10+
'Release Notes'+#10+
'License Agreement'+#10+
'InterBase Help'+#10+
'Performance Help'+#10+
'API Reference Guide'+#10+
'Data Definition Guide'+#10+
'Language Reference'+#10+
'Operations Guide'+#10+
'Programmer''s Guide'+#10+
'InterBase Tutorial'+#10+
'Communication Diagnostics'+#10+
'Server Manager'+#10+
'InterBase Windows ISQL';
ShortcutTargets = '%sBiniblicense.exe'+#10+
'%sReleasenotes.pdf'+#10+
'%sLICENSE.TXT'+#10+
'%sBinib32.hlp'+#10+
'%sBinperform.hlp'+#10+
'%sdocAPIGuideAPIGuide.pdf'+#10+
'%sdocDataDefDataDef.pdf'+#10+
'%sdocLangRefLangRef.pdf'+#10+
'%sdocOpGuideOpGuide.pdf'+#10+
'%sdocProgGdProgGd.pdf'+#10+
'%sdocTutorialTutorial.pdf'+#10+
'%sBincomdg32.exe'+#10+
'%sBinibmgr32.exe'+#10+
'%sBinwisql32.exe';
var
SLinkObject : IUnknown;
SLink : IShellLink;
PFile : IPersistFile;
StartMenuDir : pChar;
WorkingDir: pChar;
StartFolderPath: String;
ppidl : pItemIDList;
SLinkNamesList: TStringList;
SLinkTargetsList: TStringList;
WLinkName: WideString;
i: integer;
verInfo: TOSVersionInfo;
begin
{ Find out what ersion of windows we are usnig. }
verInfo.dwOSVersionInfoSize := sizeOf(verInfo);
GetVersionEx(verInfo);
SLinkNamesList := TStringList.create;
SLinkTargetsList := TStringList.create;
{ If we are isntalling on NT then the program group will
{ be a common group, else it will not since 95/98 does
{ not support this concept }
if (verinfo.dwPlatformId = VER_PLATFORM_WIN32_NT) then
SHGetSpecialFolderLocation(0, CSIDL_COMMON_PROGRAMS, ppidl)
else
SHGetSpecialFolderLocation(0, CSIDL_PROGRAMS, ppidl);
{ Create the Start menu folder (directory) }
StartMenuDir := StrAlloc(MAX_PATH);
SHGetPathFromIDList(ppidl, StartMenuDir);
StartFolderPath := StrPas(StartMenuDir) + 'InterBase';
StrDispose (StartMenuDir);
CreateDir(StartFolderPath);
{ Calculate the working directory }
WorkingDir := StrAlloc(MAX_PATH);
StrCopy(WorkingDir,PChar(DestinationDirFld.Text));
StrCat(WorkingDir,PChar('bin'));
SLinkNamesList.text := ShortcutNames;
SLinkTargetsList.text := ShortcutTargets;
{ For each element in the list check to see if the
{ target exists. If it does, then create the start menu
{ shortcut for it. }
for i := 0 to SLinkNamesList.count - 1 do
begin
if (FileExists(SLinkTargetsList.strings[i])) then
begin
SLinkObject := CreateComObject (CLSID_ShellLink);
SLink := SLinkObject as IShellLink;
PFile := SLinkObject as IPersistFile;
SLink.SetPath (pChar (Format(SLinkTargetsList.Strings[i],[DestinationDirFld.Text])));
SLink.SetWorkingDirectory (WorkingDir);
WLinkName := StartFolderPath + '' + SLinkNamesList.Strings[i] + '.lnk';
PFile.Save (PWChar (WlinkName),False);
end;
end;
{ Notify the OS that something has changed, and that it
{ should do what ever is necessary to notice it. }
SHChangeNotify (SHCNE_MKDIR, SHCNF_PATH, pChar(StartFolderPath), nil);
SLinkNamesList.destroy;
SLinkTargetsList.destroy;
MessageDlg('Shortcuts created Successfully',MtInformation,[mbok],0);
end;
{ TODO 9: Start the guardian process (optional) }
procedure TInteractiveInstallDemoFrm.StartGuardianBtnClick(Sender: TObject);
var
hManager: SC_HANDLE;
hService: SC_HANDLE;
verInfo: TOSVersionInfo;
ServiceArgs: PChar;
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
const
GUARDIAN_SERVICE_NAME = 'InterBaseGuardian';
GUARDIAN_APPLICATION_NAME = '%sbinibguard.exe';
begin
if not StartServer then
begin
verInfo.dwOSVersionInfoSize := sizeOf(verInfo);
GetVersionEx(verInfo);
{ If we are on NT, then contact the services manager and
{ ask it to start the guardian. }
if (verinfo.dwPlatformId = VER_PLATFORM_WIN32_NT) then
begin
ServiceArgs := nil;
hManager := OpenSCManager(nil,nil,SC_MANAGER_CONNECT);
hService := OpenService(hManager,GUARDIAN_SERVICE_NAME,SERVICE_START);
if (hService = 0) then
MessageDlg('Could not start Service',mtError,[mbOk],0)
else
if not StartService(hService, 0, ServiceArgs) then
if not GetLastError() = ERROR_SERVICE_ALREADY_RUNNING then
MessageDlg('Could not start Guardian Service',mtError,[mbOk],0);
CloseServiceHandle(hManager);
CloseServiceHandle(hService);
end
{ If we are on 95/98 then we must create the process. }
else
begin
StartupInfo.cb := sizeof(StartupInfo);
StartupInfo.lpReserved := nil;
StartupInfo.lpTitle:= nil;
StartupInfo.lpDesktop := nil;
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWNA;
StartupInfo.cbReserved2 := 0;
StartupInfo.lpReserved2 := nil;
if not CreateProcess(nil,PChar(Format(GUARDIAN_APPLICATION_NAME,[DestinationDirFld.Text])),
nil,nil,False,NORMAL_PRIORITY_CLASS,nil,
PChar(DestinationDirFld.Text),StartupInfo,ProcessInformation) then
MessageDlg('Could not start Guardian',mtError,[mbOk],0);
end;
end;
end;
{ TODO 10: Register Uninastall program (optional) }
procedure TInteractiveInstallDemoFrm.RegisterUninstallBtnClick(Sender: TObject);
var
retval : longint;
regHndl : HKey;
security : REGSAM;
KeyMade : Dword;
UninstallString: String;
const
RegUninstallKey = 'SOFTWAREMicrosoftWindowsCurrentVersionUninstallInterBase';
RegUninstallName = 'InterBase Demo Install/Uninstall';
RegUninstallString = '"%sInteractive_Uninstall_Demo.exe" "%s"';
begin
{ Open the registry and add an uninstall key. This will appear in the
{ Control Panel, Add/Remove Programs list. }
security := KEY_ALL_ACCESS;
retval := RegOpenKeyEx(HKEY_LOCAL_MACHINE,RegUninstallKey,0,security,regHndl);
if not (retval = ERROR_SUCCESS) then
retval := RegCreateKeyEx(HKEY_LOCAL_MACHINE,RegUninstallKey,0,'',
REG_OPTION_NON_VOLATILE,KEY_WRITE,nil,regHndl,@KeyMade);
if (retval = ERROR_SUCCESS) or (KeyMade = REG_CREATED_NEW_KEY) then
begin
if not (RegSetValueEx(regHndl,'DisplayName',0,REG_SZ,PChar(RegUninstallName),
length(RegUninstallName)+1) = ERROR_SUCCESS) then
MessageDlg('Could not create uninstall registry key',mtError,[mbOk],0);
UninstallString := Format(RegUninstallString,[DestinationDirFld.Text,UninstallFilenameFld.Text]);
if not (RegSetValueEx(regHndl,'UninstallString',0,REG_SZ,PChar(UninstallString),
length(UninstallString)+1) = ERROR_SUCCESS) then
MessageDlg('Could not create uninstall registry key',mtError,[mbOk],0)
else
MessageDlg('Uninstall Registered Successfully',MtInformation,[mbok],0);
end
else
MessageDlg('Could not create uninstall registry key',mtError,[mbOk],0);
RegCloseKey(regHndl);
end;
{**********************************}
{ Private Functions and procedures }
{**********************************}
function TInteractiveInstallDemoFrm.DisplayErrorMessage(handle: OPTIONS_HANDLE; error: MSG_NO): word;
var
msg: PChar;
begin
msg := StrAlloc(ISC_INSTALL_MAX_MESSAGE_LEN);
isc_install_get_message(handle, error, msg, ISC_INSTALL_MAX_MESSAGE_LEN);
if (error < isc_install_success) then
result := MessageDlg(Strpas(msg),mtWarning,[mbOK, mbIgnore],0)
else
result := MessageDlg(Strpas(msg),mtWarning,[mbOK],0);
StrDispose(msg);
end;
{****************************************************}
{ Call-back functions for Error and status reporting }
{****************************************************}
{ TODO 6: Setup Error Handling Callback function }
function DisplayError(msg: MSG_NO; data: pointer; error_msg: TEXT): integer;
var
retval: word;
begin
if (not InteractiveInstallDemoFrm.Cancelling) then
begin
retval := mrIgnore;
if (msg = isc_install_fp_copy_delayed) or (msg = isc_install_fp_delete_delayed) then
InteractiveInstallDemoFrm.RebootMessage := true
else
retval := InteractiveInstallDemoFrm.DisplayErrorMessage
(InteractiveInstallDemoFrm.OptionsHandle, msg);
if retval = mrIgnore then
result := isc_install_fp_continue
else
begin
InteractiveInstallDemoFrm.Cancelling := true;
result := isc_install_fp_abort;
end;
end
else
result := isc_install_fp_abort;
end;
{ TODO 6: Setup Status Handling Callback function }
function DisplayStatus(status: integer; data: pointer; const status_msg: TEXT): integer;
begin
InteractiveInstallDemoFrm.InstallProgressGauge.Progress := status;
Application.ProcessMessages;
result := isc_install_fp_continue;
end;
end.
B: Demo Install Program Source
unit Uninstall_Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Gauges, StdCtrls, IBInstall, ComCtrls, ShlObj, ExtCtrls;
type
TInteractiveUninstallDemoFrm = class(TForm)
Step1GrpBx: TGroupBox;
UninstallFilenameFld: TEdit;
PerformUninstallBtn: TButton;
UninstallProgressGauge: TGauge;
Step2GrpBx: TGroupBox;
Step3GrpBx: TGroupBox;
RemoveUninstallEntryBtn: TButton;
RemoveStartMenuShortcutsBtn: TButton;
procedure PerformUninstallBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure RemoveUninstallEntryBtnClick(Sender: TObject);
procedure RemoveStartMenuShortcutsBtnClick(Sender: TObject);
private
{ Private declarations }
RebootMessage: Boolean;
Cancelling: Boolean;
function DisplayErrorMessage(error: MSG_NO): word;
public
{ Public declarations }
end;
function DisplayError(msg: MSG_NO; data: pointer; error_msg: TEXT): integer; export; stdcall;
function DisplayStatus(status: integer; data: pointer; const status_msg: TEXT): integer; export; stdcall;
var
InteractiveUninstallDemoFrm: TInteractiveUninstallDemoFrm;
implementation
{$R *.DFM}
procedure TInteractiveUninstallDemoFrm.FormCreate(Sender: TObject);
begin
if ParamCount >= 1 then
UninstallFilenameFld.Text := ParamStr(1);
end;
{*****************************************}
{ Three easy steps to uninstall InterBase }
{*****************************************}
{ TODO 1: Perform Uninstall }
procedure TInteractiveUninstallDemoFrm.PerformUninstallBtnClick(Sender: TObject);
var
err_no: MSG_NO;
begin
{ Ask the install API to undo what ever it logged in the uninstall file. }
err_no := isc_uninstall_execute(PChar(UninstallFileNameFld.text),
@DisplayStatus, nil, @DisplayError, nil);
if (err_no <> isc_install_success) then
begin
DisplayErrorMessage(error);
end;
{ Display a message if there is a need to reboot. This might be needed if
{ some files could not be copied because they were in use. In this case they
{ were set up for a delayed copy, and this will be done on the next reboot. }
if (RebootMessage) then
MessageDlg('You must reboot in order for uninstall to finish!',mtInformation, [mbok], 0)
else
MessageDlg('Uninstalled Successfully!',mtInformation, [mbok], 0);
end;
{ TODO 2: Unregiter uninstall (optional) }
procedure TInteractiveUninstallDemoFrm.RemoveUninstallEntryBtnClick(
Sender: TObject);
var
security : REGSAM;
regHndl : HKey;
retval: longint;
begin
{ Since we are now uninstalled, we should remove the uninstall entry
{ from the list in Control Panel | Add/Remove Programs which was added
{ by the install program. }
security := KEY_ALL_ACCESS;
retval := RegOpenKeyEx(HKEY_LOCAL_MACHINE,nil,0,security,regHndl);
if (retval = ERROR_SUCCESS) then
begin
RegDeleteKey(HKEY_LOCAL_MACHINE,'SOFTWAREMicrosoftWindowsCurrentVersionUninstallInterBase');
end;
RegCloseKey(regHndl);
end;
{ TODO 3: Remove Shortcuts (optional) }
procedure TInteractiveUninstallDemoFrm.RemoveStartMenuShortcutsBtnClick(
Sender: TObject);
const
RootShortcutNames = 'Communication Diagnostics'+#10+
'InterBase Configuration Tool'+#10+
'InterBase Guardian'+#10+
'InterBase Server'+#10+
'License Registration Tool'+#10+
'Server Manager'+#10+
'InterBase Windows ISQL'+#10+
'Release Notes'+#10+
'License Agreement';
DocsShortcutNames = 'InterBase Help'+#10+
'Performance Help'+#10+
'API Reference Guide'+#10+
'Data Definition Guide'+#10+
'Language Reference'+#10+
'Operations Guide'+#10+
'Programmer''s Guide'+#10+
'InterBase Tutorial';
var
verInfo: TOSVersionInfo;
StartMenuDir: pChar;
StartFolderPath: String;
ppidl: pItemIDList;
SLinkNamesList: TStringList;
i: integer;
begin
{ In the same way that the install program added the start menu
{ shortcuts, the uninstall should remove them. }
verInfo.dwOSVersionInfoSize := sizeOf(verInfo);
GetVersionEx(verInfo);
if (verinfo.dwPlatformId = VER_PLATFORM_WIN32_NT) then
SHGetSpecialFolderLocation(0, CSIDL_COMMON_PROGRAMS, ppidl)
else
SHGetSpecialFolderLocation(0, CSIDL_PROGRAMS, ppidl);
StartMenuDir := StrAlloc(MAX_PATH);
SHGetPathFromIDList(ppidl, StartMenuDir);
StartFolderPath := StrPas(StartMenuDir) + 'InterBase';
StrDispose (StartMenuDir);
SLinkNamesList := TStringList.create;
SLinkNamesList.text := RootShortcutNames;
for i := 0 to SLinkNamesList.count - 1 do
DeleteFile(StartFolderPath + '' + SLinkNamesList.Strings[i] + '.lnk');
SLinkNamesList.text := DocsShortcutNames;
for i := 0 to SLinkNamesList.count - 1 do
DeleteFile(StartFolderPath + 'Documentation' + SLinkNamesList.Strings[i] + '.lnk');
RemoveDir(StartFolderPath + 'Documentation');
RemoveDir(StartFolderPath);
end;
{**********************************}
{ Private Functions and procedures }
{**********************************}
function TInteractiveUninstallDemoFrm.DisplayErrorMessage(error: MSG_NO): word;
var
msg: PChar;
begin
msg := StrAlloc(ISC_INSTALL_MAX_MESSAGE_LEN);
isc_install_get_message(0, error, msg, ISC_INSTALL_MAX_MESSAGE_LEN);
if (error < isc_install_success) then
result := MessageDlg(Strpas(msg),mtWarning,[mbOK, mbIgnore],0)
else
result := MessageDlg(Strpas(msg),mtWarning,[mbOK],0);
StrDispose(msg);
end;
{****************************************************}
{ Call-back functions for Error and status reporting }
{****************************************************}
{ TODO 1: Supply an error handling call-back function (optional) }
function DisplayError(msg: MSG_NO; data: pointer; error_msg: TEXT): integer;
var
retval: word;
begin
if not InteractiveUninstallDemoFrm.Cancelling then
begin
retval := mrIgnore;
if (msg = isc_install_fp_copy_delayed) or (msg = isc_install_fp_delete_delayed) then
InteractiveUninstallDemoFrm.RebootMessage := true
else
retval := InteractiveUninstallDemoFrm.DisplayErrorMessage(msg);
if retval = mrIgnore then
result := isc_install_fp_continue
else
begin
InteractiveUninstallDemoFrm.Cancelling := true;
result := isc_install_fp_abort;
end;
end
else
result := isc_install_fp_abort;
end;
{ TODO 1: Supply a status handling call-back function (options) }
function DisplayStatus(status: integer; data: pointer; const status_msg: TEXT): integer;
begin
InteractiveUninstallDemoFrm.UninstallProgressGauge.Progress := status;
Application.ProcessMessages;
result := isc_install_fp_continue;
end;
end.
Connect with Us