Sample Support DLL (Pascal)

A C version of this DLL is also available.

{***************************************************************}
{                                                               }
{       DeployMaster Sample Support DLL                         }
{                                                               }
{***************************************************************}

library Support;

uses
  Windows,
  SysUtils;

{$R *.RES}

// These variables are needed by the sample routines, but are invisible to DeployMaster Setup
var
  IdentityKey: HKey;
  DelphiInstalled: array[2..5] of Boolean;


(************************************************************
 ***                                                      ***
 ***                 INSTALLATION STARTUP                 ***
 ***                                                      ***
 ************************************************************)

// This routine, if present in the DLL, is the first one that is called by Setup
// It will be called after the user has clicked one of the buttons to start the install,
// and after the installer has been elevated to administrator privileges,
// but before anything has actually been installation.
// It gives you the chance to abort the installation early on if something is wrong with the user's system
// If AllowInstallation3() returns False, the Setup will terminate right away.
// So it is the responsibility of AllowInstallation3 to inform the user of what is wrong (by means of a message box).
// If it returns True, it should keep quiet.
function AllowInstallation3(DeployWindow: HWND; Portable, CurrentUser: BOOL): BOOL; stdcall;
begin
  // Check if Borland Delphi is running and warn user that we don't like this.
  // Give the user the chance to close it without having to stop and restart the setup application
  Result := True;
  while Result and (FindWindow('TAppBuilder', nil) <> 0) do
    Result :=  MessageBox(DeployWindow, 'DeployMaster has detected that Delphi is still running.'#13 +
                          'Please close it (and any other open applications) and then click OK'#13 +
                          'To cancel the setup, click Cancel',
                          'DeployMaster', MB_ICONHAND or MB_OKCANCEL) = IDOK;
  if Portable then
    IdentityKey := 0
  else if CurrentUser then
    IdentityKey := HKEY_CURRENT_USER
  else
    IdentityKey := HKEY_LOCAL_MACHINE
end;


(************************************************************
 ***                                                      ***
 ***                FILE SELECTION SUPPORT                ***
 ***                                                      ***
 ************************************************************)

// This routine, if present in the DLL, is called after AllowInstallation3() (if present) has returned True
// It should fill the Folders character array (which is pre-allocated by Setup),
// with any additional system folders that Setup may need
// System folders are folders that cannot be changed by the user.
// BufferSize is the number of bytes that have been allocated for Folders and is $8000 by default
// These are generally folders into which other software has already been installed,
// which your software will cooperate with.
// The format is %TAGNAME1%Fullpath1%TAGNAME2%Fullpath2 ...
// Fullpath must not contain any tags and may be preceded by an * to mark the folder as a shared one
// Files placed in shared folders have their reference counts updated in
// HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\SharedDLLs
// Fullpath must not contain a trailing backslash
procedure GetSystemFolders(Folders: PChar; BufferSize: Integer); stdcall;
var
  I: Integer;
  DelphiKey: HKey;
  ZStr: array[0..MAX_PATH] of Char;
  BufType, BufSize: Integer;
begin
  // Try to find the folders into which Delphi 2 through 5 have been installed
  for I := 2 to 5 do begin
    if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar('Software\Borland\Delphi\' + IntToStr(I) + '.0'),
                    0, KEY_READ, DelphiKey) = 0 then begin
      BufSize := SizeOf(ZStr);
      StrCat(Folders, PChar('%DELPHI' + IntToStr(I) + '%'));
      if RegQueryValueEx(DelphiKey, 'RootDir', nil, @BufType, @ZStr, @BufSize) = 0 then
        StrCat(Folders, ZStr)
      else
        StrCat(Folders, PChar('C:\Program Files\Borland\Delphi' + IntToStr(I)));
      DelphiInstalled[I] := True;
    end
    else DelphiInstalled[I] := False;
  end;
end;

// This routine, if present in the DLL, is called after GetSystemFolders() is called
// It is called whether GetSystemFolders() is present or not
// It should fill the Folders character array (which is pre-allocated by Setup),
// with any additional installation folders that Setup may need.
// BufferSize is the number of bytes that have been allocated for Folders and is $8000 by default.
// These folders can be completely changed by the user by clicking on the Advanced Installation
// button in Setup.
// The format is %TAGNAME1%|Description1|Fullpath1|%TAGNAME2%|Description2|Fullpath2| ...
// Fullpath may start with a tag defined in GetSystemFolders, or one of the following tags:
// %PROGRAMFILES%, %COMMONFILES%, %STARTMENU%, %PROGRAMSMENU%, %DESKTOP%, %SENDTO%, %STARTUP%, %WINDOWS%, %SYSTEM%
// Fullpath may be preceded with an * to mark the folder as a shared one.
// Files placed in shared folders have their reference counts updated in
// HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\SharedDLLs
// Fullpath must not contain a trailing backslash
// If Folders is not empty, then the last character in the Folders string must be a | (pipe symbol)
procedure GetInstallFolders(Folders: PChar; BufferSize: Integer); stdcall;
var
  I: Integer;
begin
  for I := 2 to 5 do
    if DelphiInstalled[I] then
      // eg: '%D5VCL%|Delphi 5 binaries|%DELPHI5%\Lib|'
      StrCat(Folders, PChar('%D' + IntToStr(I) + 'VCL%|Delphi ' + IntToStr(I) + ' binaries|%DELPHI' + IntToStr(I) + '%\Lib|'))
end;

// If present in the support DLL, GetComponentSelection() is called for each component in the setup package.
// These calls happen after the calls to GetSystemFolders() and GetInstallFolders()
// The first parameter points to the name of the component, and must not be modified by the routine
// Selectable determines whether the user can select whether this component will be installed or not
// Selected determines whether the component is selected by default or not
// If the user is freshly installing your software package, Selectable and Selected default to the settings you made
// in DeployMaster Builder when building the setup package, and IsInstalled is False
// If the user is updating this package, Selected equals to IsInstalled if the installation status could be determined,
// which indicates whether the component is currently installed or not. If the installation status could not be determined,
// IsInstalled is False and Selected is set to the setting you made in DeployMaster Builder
// The routine should set Selectable and Selected to meaningful values, depending on the configuration of the user's system
procedure GetComponentSelection(ComponentName: PChar; var Selectable, Selected: Bool; IsInstalled: Bool); stdcall;
begin
  // Don't install files for a certain Delphi version, if that Delphi version is not installed.
  // Setting Selectable to False as well, prevents the user from overriding our decision.
  if StrIComp(ComponentName, 'Delphi2') = 0 then begin
    Selected := DelphiInstalled[2]; Selectable := Selected;
  end
  else if StrIComp(ComponentName, 'Delphi3') = 0 then begin
    Selected := DelphiInstalled[3]; Selectable := Selected;
  end
  else if StrIComp(ComponentName, 'Delphi4') = 0 then begin
    Selected := DelphiInstalled[4]; Selectable := Selected;
  end
  else if StrIComp(ComponentName, 'Delphi5') = 0 then begin
    Selected := DelphiInstalled[5]; Selectable := Selected;
  end;
end;


(************************************************************
 ***                                                      ***
 ***                  IDENTITY SUPPORT                    ***
 ***                                                      ***
 ************************************************************)

// This routine is called right before the identity screen is shown to the user.
// It is called before any of the other identity functions.
// It allows it to figure out where the processed information should be stored in the Windows registry.
// CompanyName and AppVersion may be nil, AppName will always have a value.
// The strings must not be modified by the DLL, and their length is arbitrary.
procedure InitIdentity(CompanyName, AppName, AppVersion: PChar); stdcall;
var
  ZStr: array[0..255] of Char;
begin
  if IdentityKey <> 0 then begin
    ZStr := 'Software';
    if CompanyName <> nil then begin
      StrCat(ZStr, '\');
      StrCat(ZStr, CompanyName);
    end;
    StrCat(ZStr, '\');
    StrCat(ZStr, AppName);
    if RegCreateKeyEx(HKEY_CURRENT_USER, ZStr, 0, nil, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nil, IdentityKey, nil) <> 0 then
      IdentityKey := 0;
  end;
end;

// ValidIdentity() is called when the user clicks on the Proceed button.
// Should return True if the information is valid and the user may proceed.  If it returns False, the user will have to retry.
// The parameters for items the user is not requested to specify will be nil
// ValidIdentity() is allowed to modify the contents of the strings; e.g. to clear out an invalid registration code
function ValidIdentity(Name, Company, Serial, RegCode: PChar): Bool; stdcall;
begin
  Result := True;
end;

// LoadIdentity() is called right before the user is allow to supply his information
// If (another version of) the application is already installed, it should set the strings to the previously entered data
// If not, the DLL has a chance to provide default data (e.g. a time-limited trial mode registration IdentityKey)
// The parameters for items the user is not requested to specify will be nil
procedure LoadIdentity(Name, Company, Serial, RegCode: PChar); stdcall;
var
  BufType, BufSize: Integer;
begin
  if IdentityKey <> 0 then begin
    BufSize := 128;
    if Name <> nil then RegQueryValueEx(IdentityKey, 'Name', nil, @BufType, PByte(Name), @BufSize);
    BufSize := 128;
    if Company <> nil then RegQueryValueEx(IdentityKey, 'Company', nil, @BufType, PByte(Company), @BufSize);
    BufSize := 128;
    if Serial <> nil then RegQueryValueEx(IdentityKey, 'Serial', nil, @BufType, PByte(Serial), @BufSize);
    BufSize := 128;
    if RegCode <> nil then RegQueryValueEx(IdentityKey, 'RegCode', nil, @BufType, PByte(RegCode), @BufSize);
  end;
end;

// SaveIdentity() is called after the user clicked the proceed button and ValidIdentity() returned true.
// It should write the data to the Windows registry, so that the application, once installed, can use it.
// The parameters for items the user is not requested to specify will be nil
procedure SaveIdentity(Name, Company, Serial, RegCode: PChar); stdcall;
begin
  if IdentityKey <> 0 then begin
    // Save data
    if Name <> nil then RegSetValueEx(IdentityKey, 'Name', 0, REG_SZ, Name, StrLen(Name)+1);
    if Company <> nil then RegSetValueEx(IdentityKey, 'Company', 0, REG_SZ, Company, StrLen(Company)+1);
    if Serial <> nil then RegSetValueEx(IdentityKey, 'Serial', 0, REG_SZ, Serial, StrLen(Serial)+1);
    if RegCode <> nil then RegSetValueEx(IdentityKey, 'RegCode', 0, REG_SZ, RegCode, StrLen(RegCode)+1);
    // Clean up
    RegCloseKey(IdentityKey);
  end;
end;


(************************************************************
 ***                                                      ***
 ***                  FINISHING TOUCHES                   ***
 ***                                                      ***
 ************************************************************)

// FinishDeployment() is called after DeployMaster has finished its job.
// Log will contain the filename of the deployment log that DeployMaster has written to disk
// The most important task of FinishDeployment() is to update any configuration files the application uses,
// so it can find its own files in the case it does not use the deployment log itself for this purpose
// In case of a portable installation, there will be no log.  In that case,
// the Log parameter will be the path to the RemovableDrive.sys file in the installation folder
// on the removable device (even if you disabled the option to create that file).
procedure FinishDeployment(Log: PChar); stdcall;
begin
end;


exports
  // Make our support routines visible to the world
  // If we're using Unicode, we need to add W to the names of the exported functions
  AllowInstallation3, { no Unicode version }
  GetSystemFolders {$IFDEF UNICODE}name 'GetSystemFoldersW'{$ENDIF}, 
  GetInstallFolders {$IFDEF UNICODE}name 'GetInstallFoldersW'{$ENDIF},
  GetComponentSelection {$IFDEF UNICODE}name 'GetComponentSelectionW'{$ENDIF},
  InitIdentity {$IFDEF UNICODE}name 'InitIdentityW'{$ENDIF}, 
  ValidIdentity {$IFDEF UNICODE}name 'ValidIdentityW'{$ENDIF}, 
  LoadIdentity {$IFDEF UNICODE}name 'LoadIdentityW'{$ENDIF}, 
  SaveIdentity {$IFDEF UNICODE}name 'SaveIdentityW'{$ENDIF},
  FinishDeployment {$IFDEF UNICODE}name 'FinishDeploymentW'{$ENDIF};

end.