Inno Setup Extensions Knowledge Base

Build on 2005-10-16

Article List.

This knowledge base is born to help programmers to make installer packages using Inno Setup Extensions.

Inno Setup, by Jordan Russell, is a free installer for Windows programs. First introduced in 1997, Inno Setup today rivals and even surpasses many commercial installers in feature set and stability.

My Inno Setup Extensions, by Martijn Laan, is based on Inno Setup and adds 'Pascal scripting' to Inno Setup. The Pascal scripting feature adds lots of new possibilities to customize your Setup at run-time.
Pascal Scripting engine is Innerfuse Pascal Script by Carlo Kok.

If you are a newbie, or expert, of IS and ISX take a look at Inno Setup Assimilation Report by Michael Reitz.

This knowledge base collect tips, code samples, links and info on how do standard and special tasks in an installer, check news to see latest atricles added and updated.
If you have a code sample, a tip about IS and ISX, and you want to share it, send them to me and I'll add it to this ISX KB.

All features of Inno Setup and My Inno Setup Extensions are explained in help file installed with the compiler.
Take a look also at sample directory, in your My Inno Setup Extensions install folder, there you can find samples on how use IS and code samples for ISX.

A good source of information on common installation tasks/problems are FAQ and HOW TO on Jordan website.

Another source of infos and experiences are newsgroups on IS and ISX, it's possible read them with a newsreader, via web and also it's possible search inside them, before post on these newsgroups look at usage guidelines.

Enjoy.

Vincenzo Giordano


Articles List

  • 1 - How to detect version of MDAC installed
  • 2 - How to detect IE version installed
  • 3 - How to create a virtual directory for IIS
  • 4 - How to run MSI package
  • 5 - How to detect if windows installer engine is installed
  • 6 - How to detect DirectX version
  • 7 - PSVince is a DLL to detect if a module is loaded in memory
  • 8 - How to detect if an application is running
  • 9 - How to kill a process
  • 10 - How to detect vbscript runtime wscript.exe
  • 11 - How to detect current display resolution and color depth
  • 12 - DATViewer
  • 13 - How to detect IIS version installed
  • 14 - How to verify the program is already installed
  • 15 - How to replace a line in a text file
  • 16 - How to add a line in a text file
  • 17 - How to detect IE version installed (2)
  • 18 - How to detect ADSI version installed
  • 19 - How to get drive type
  • 20 - How to get drive volume and serial number
  • 21 - How to detect if and which version of MS Access is installed
  • 22 - How to detect if and which version of SQL Server or MSDE is installed
  • 23 - Sample MSDE 2.0 install script
  • 24 - How to create a ODBC DSN
  • 25 - Additional registry functions
  • 26 - How to detect Phisical memory
  • 27 - How to detect filesystem type
  • 28 - How to logoff if user is not Administrator
  • 29 - How to prompt for an additional directory for data
  • 30 - How to detect first CD-ROM drive in the system
  • 31 - How to create a MTS (COM+) package
  • 32 - Functions to Start, Stop, Install, Remove a NT Service
  • 33 - How to create a ODBC DSN (2)
  • 34 - How to detect Acrobat Reader version installed
  • 35 - How to add a printer
  • 36 - How to install a screensaver
  • 37 - How to browse for a file
  • 38 - How to detect Excel is running by window class name
  • 39 - Two utilities for environment variables, hosts and services changing
  • 40 - Better way to install MSDE
  • 41 - How to check for JDK
  • 42 - How to ask for a drive to install
  • 43 - How to prevent user to choose an install folder with spaces
  • 44 - Setting ScreenSaver on/off and updating timeout after install
  • 45 - Native ISX procedures for PATH modification
  • 46 - Sample of make different actions in different cases
  • 47 - How to store date and time of installation in registry
  • 48 - How to set a date time limit for the installer
  • 49 - How to detect Acrobat Reader search plug-in version
  • 50 - How to to retrieve the Printer Driver Directory
  • 51 - How to remove a web share at uninstall time
  • 52 - Library to access ODBC functions
  • 53 - How to test a SQL Server connection using ODBC driver
  • 54 - How to list User and Sytem DSN
  • 55 - How to install Windows Media Player 9
  • 56 - Add a button to License page to print it
  • 57 - How to show format media dialog
  • 58 - Replace a line in a text file with comment check
  • 61 - Libray for NetAPI functions.
  • 59 - How to create a drive map using the network connection dialog box
  • 60 - Files to install SQL-DMO for SQL Server 2000
  • 62 - How to add a local user
  • 63 - Sample script to help understand when and which event is triggered
  • 64 - Library to format date and time
  • 65 - How to format date and time
  • 66 - How to translate custom messages and labels on a multilingual setup
  • 67 - How To: SplashScreen with Inno Setup 4.1.3
  • 68 - How to extract script source from a compiled setup
  • 69 - Compile different builds in a snap
  • 70 - Sample script to install Crystal Report 8 runtime files
  • 71 - How to check for .NET framework for Inno Setup 4
  • 72 - Function CopyFolder
  • 73 - How to detect Acrobat Reader version installed (2)
  • 74 - DirExists function to use in pre-processor directives
  • 78 - How to create a virtual FTP directory for IIS
  • 75 - How to open HTML page with MS IE
  • 76 - How to detect MS Access Version (2)
  • 77 - How to get Date and Time of a file
  • 79 - How to close an application posting a QUIT message
  • 80 - How to detect if JET engine is installed
  • 81 - Modified the Test Events sample so that it works with Inno Setup 5
  • 82 - How to check for .NET framework for Inno Setup 5
  • 83 - Test Events for Inno Setup 5 with Uninstall events logged
  • 85 - How to put the current Java version at the beginning of the PATH
  • 86 - How to create an upgrade
  • 87 - How to test a SQL Server connection with ADO
  • 88 - How to create and modify an Access MDB with ADO
  • 89 - Validating Windows Username and Password
  • 90 - How to ask for a drive to install for IS 5
  • 91 - How to prompt for an additional folder for data for IS 5
  • 92 - How to call psvince.dll on install and uninstall
  • 93 - How to browse for a machine/host
  • 94 - How to include a second license page
  • 95 - Set Environment variables in Windows 9x, NT, 2000 or XP
  • 96 - HowTo change the mouse cursor of WizardForm
  • 97 - How to ask a password to uninstall
  • 99 - How to test a SQL Server connection using ODBC driver for IS 5
  • 100 - Add a button to License page to print it for IS 5
  • 101 - Create an auto increment build number
  • 103 - Add to the setup name the build date and time
  • 104 - How to get Date and Time of a file for IS 5
  • 105 - How do I get the list of files from the [Files] section into the [Code] section
  • 106 - Custom message box
  • 107 - How to detect if an application is running with code
  • 108 - PostgreSQL setup built with Inno Setup
  • 109 - Read [Files] section from [Code], One function
  • 110 - List of files from the [Files] section into the [Code] section (update)


Credits

Jordan Russell for Inno Setup.

Martijn Laan for My Inno Setup Extensions.

Carlo Kok for Innserfuse Pascal Script.

Scripts syntax color made with Syn text editor http://syn.sourceforge.net


Web Links - Main

Add-ins | Compilers
Documentation | Implementation
Microsoft Downloads Run-times and other stuffs from Microsoft | Microsoft Info Documents, information and files about files that can or cannot be redist.
Miscellanea | Multi Language
Other Installers | Script Editors-Generators Tools to help creation and managment of Inno Setup scripts.
Tools | Windows Installer Windows Installer MSI files related


1 - How to detect version of MDAC installed

Author: Vincenzo
Article created: 21/05/02 21:04:28
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=1

This script is useful to detect if, and which version of MDAC is present on the machine.
This sample check if there is at least MDAC version 2.60, if not setup cannot continue.

See also:

HOWTO: Install DCOM & MDAC on Inno Setup Knowledge Base.

How to check for MDAC version and
Microsoft Data Access Components (MDAC) release history on Microsoft Knowlede Base

;
; ISX 3.0.2
;
;
[Setup]
AppName=MDAC
AppVerName=MDAC
DefaultDirName={pf}\MDAC
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=MDAC

[Code]

procedure DecodeVersion( verstr: String; var verint: array of Integer );
var
  i,p: Integer; s: string;
begin
  // initialize array
  verint := [0,0,0,0];
  i := 0;
  while ( (Length(verstr) > 0) and (i < 4) ) do
  begin
      p := pos('.', verstr);
      if p > 0 then
      begin
      if p = 1 then s:= '0' else s:= Copy( verstr, 1, p - 1 );
        verint[i] := StrToInt(s);
        i := i + 1;
        verstr := Copy( verstr, p+1, Length(verstr));
      end
      else
      begin
        verint[i] := StrToInt( verstr );
        verstr := '';
      end;
  end;

end;

function GetMDACVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ExpandConstant('{cf}\System\Ado\msado15.dll') , sVersion );
  Result := sVersion;
end;

// This function compares version string
// return -1 if ver1 < ver2
// return  0 if ver1 = ver2
// return  1 if ver1 > ver2
function CompareVersion( ver1, ver2: String ) : Integer;
var
  verint1, verint2: array of Integer;
  i: integer;
begin

  SetArrayLength( verint1, 4 );
  DecodeVersion( ver1, verint1 );

  SetArrayLength( verint2, 4 );
  DecodeVersion( ver2, verint2 );

  Result := 0; i := 0;
  while ( (Result = 0) and ( i < 4 ) ) do
  begin
      if verint1[i] > verint2[i] then
        Result := 1
      else
      if verint1[i] < verint2[i] then
          Result := -1
        else
          Result := 0;

      i := i + 1;
  end;

end;

function InitializeSetup(): Boolean;
begin

  if CompareVersion( GetMDACVersion(), '2.60') < 0 then
  begin
    MsgBox( 'There is MDAC version: ' + GetMDACVersion() +
      ' it is necessary to install version 2.60.',  mbInformation, MB_OK );
    Result := false;
  end
  else
    Result := true;
end;

Created on 20 12 2006 - Inno Setup Extensions Knowledge Base - Vincenzo Giordano


2 - How to detect IE version installed

Article created: 21/05/02 21:04:30
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=2

This script is useful to detect if, and which version of IE Internet Explorer is present on the machine.
See also MSKB 164539: How to determine which version of Internet Explorer is installed

;
; ISX 3.0.4
;
[Setup]
AppName=IEVersion
AppVerName=IEVersion
DefaultDirName=IEVersion
DisableStartupPrompt=true
Uninstallable=false
CreateAppDir=false

[Code]

function GetIExplorerVersion(): String;
var
  sVersion:  String;
begin
  RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Internet Explorer', 'Version', sVersion );
  Result := sVersion;
end;

function InitializeSetup(): Boolean;
begin
  MsgBox( 'Version of Internet Explorer is (' + GetIExplorerVersion() + ')',
      mbInformation, MB_OK );
  Result := false;
end;


3 - How to create a virtual directory for IIS

Article created: 21/05/02 21:05:25
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=3

This script shows how to create a virtual web dir on IIS.
There are some checks on setup initialize to check presence of IIS, ADSI and wscript, all these components are already present on an Windows 2000 box with IIS 5.0 installed.
The core of this sample is a vbs script that uses ADSI to add new virtual web dir, dir to create is the first parameter of the script, virtaul dir name is MyShare, but can be passes as parameter modifing the script.

See also: How to create a virtual FTP directory for IIS

[Setup]
AppName=WebDir
AppVerName=WebDir
DefaultDirName={sd}\WebDir
DisableStartupPrompt=true
Uninstallable=false
DisableProgramGroupPage=true

[Files]
Source: AddWebIIS.vbs; DestDir: {tmp}

[Run]
Filename: {tmp}\AddWebIIS.vbs; WorkingDir: {tmp}; Description: Create virtual dir; StatusMsg: Create virtual dir; Flags: shellexec skipifdoesntexist; Parameters: {app}

[Code]

// check functions

function GetIISVersion(): String;
var  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ExpandConstant('{sys}\inetsrv\inetinfo.exe') , sVersion );
  Result := sVersion;
end;

function GetWScriptVersion(): String;
var sVersion, fname:  String;
begin
  sVersion := '';
  if UsingWinNT
    then fname := ExpandConstant('{sys}\wscript.exe')
    else fname := ExpandConstant('{win}\wscript.exe');
  GetVersionNumbersString( fname , sVersion );
  Result := sVersion;
end;

function GetADSIVersion(): String;
var  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ExpandConstant('{sys}\adsnt.dll') , sVersion );
  Result := sVersion;
end;

function InitializeSetup(): Boolean;
begin
  Result := true;

  // check for IIS
  if Length(GetIISVersion()) = 0 then begin
    MsgBox( 'IIS is not present', mbInformation, MB_OK );
  Result := false;
  end;

  // check for escipt
  if Result then
    if Length(GetWScriptVersion()) = 0 then begin
      MsgBox( 'Wscript is not present', mbInformation, MB_OK );
    Result := false;
    end;

  // check for ADSI
  if (Result) then
    if Length(GetADSIVersion()) = 0 then begin
      MsgBox( 'ADSI is not present', mbInformation, MB_OK );
    Result := false;
    end;

end
AddWebIIS.vbs

'
' Create Virtual Dir in IIS
' Tested on: IIS 4.0 and IIS 5.0
' Autore: Vincenzo Giordano
' email: vincenzog(at)libero.it
' Data: 13 December 2001
'

' All variables must be created explicitly
Option Explicit

Dim sName, sPath

'name of virtual dir
sName = "MyShare"

'first parameter is dir to share
if WScript.Arguments.Count > 0 then
   spath = WScript.Arguments(0)
else
   'no parameters so set a default dir
   sPath = "C:\InetPub\wwwroot"
end if

Dim oIIS, oDir
Set oIIS = GetObject("IIS://localhost/W3SVC/1/Root")
On Error Resume Next
Set oDir = oIIS.GetObject("IISWebVirtualDir", sName)
' This will return error -2147024893 if it doesn't exist
If Err.Number <> 0 Then Set oDir = oIIS.Create("IISWebVirtualDir", sName)
Err.Clear
oDir.AccessScript = True
oDir.Path = sPath

' Workaround for bug in IIS4
oDir.KeyType = "IIsWebVirtualDir"

oDir.SetInfo
oDir.AppCreate True
oDir.SetInfo
Set oDir = Nothing
Set oIIS = Nothing


4 - How to run MSI package

Article created: 21/05/02 21:14:57
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=4

It is possible to include and run a MSI package with InnoSetup.
The code show how to run a MSI package included in main setup or in same directory of setup.exe.

These two articles of MSKB shows which command line parameters are accepted by msiexec.exe
MSKB 227091 Command-Line Switches for the Microsoft Windows Installer Tool
MSKB 314881 The Command-Line Options for the Microsoft Windows Installer Tool Msiexec.exe

It is also useful to know how to pass a parameter with a specified value to the MSI pacakge, see
MSKB 230781 How to Use Property Reference Command-Line Parameters with Msiexec.exe

[Files]
Source: MyPackage.msi; DestDir: {tmp}
[Run]
Filename: {%COMSPEC}; Parameters: /C msiexec -i {tmp}\MyPacakge.msi; WorkingDir:{tmp};  StatusMsg: "Installing my package"
;
; or
;
[Run]
Filename: msiexec.exe; Parameters: "-i ""{src}\MyPacakge.msi"" -qn"; WorkingDir: "{src}"; StatusMsg: "Installing my package"


5 - How to detect if windows installer engine is installed

Article created: 21/05/02 21:14:54
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=5

This script is useful to detect if, and which version of MSKB 242479 Windows Installer engine is present on the machine.
This check is performed looking for msiexec.exe, if it doesn't exists user can go directly to a Microsoft website to download and install it.

;
;ISX 2.0.18
;
[Setup]
AppName=MSIVersion
AppVerName=MSIVersion
DefaultDirName={pf}\MSIVersion
DisableStartupPrompt=true
Uninstallable=false

[Code]

//INFO: MSI: How to Obtain the Windows Installer Engine (Q292539)
//http://support.microsoft.com/default.aspx?scid=kb;en-us;Q292539

const
  msiurl = 'http://support.microsoft.com/default.aspx?scid=kb;en-us;Q292539';

function InitializeSetup(): Boolean;
var msiver: String; answer, errcode: Integer;
begin
  msiver := GetMSIVersion();
  if Length( msiver ) = 0 then
  begin
    answer := MsgBox( 'It is not present Windows Installer Engine, install it before continue with this setup.' +#13#13 +
      'Do you want to go to Microsoft website to download and install it ?',
      mbConfirmation, MB_YESNO );

  // if user choosed yes goto website
  if answer = IDYES then
    InstShellExec( msiurl,'', '', SW_SHOWMAXIMIZED, errcode );

  // cannot go with setup anyway
  Result := false;
  end
  else
    // go with setup
  Result := true;
end;

function GetMSIVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ChangeDirConst('{sys}\msiexec.exe') , sVersion );
  Result := sVersion;
end;


6 - How to detect DirectX version

Article created: 25/05/02 22:00:30
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=6

This script at startup of setup check DirectX version, if version is not 4.08 warn user, and if user doesn't want to procede suggest to upgrade DirectX from Microsoft website http://www.microsoft.com/directx.

DirectX version is stored in registry as 4.majorversion.minorversion
DirectX 8.0 is 4.8.0
DirectX 8.1 is 4.8.1
DirectX 9.0 is 4.9.0

Based on code provided by Mike C., NVRefreshTool

[Setup]
AppName=DirectX
AppVerName=DirectX
DefaultDirName={pf}\DirectX
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=DirectX
CreateAppDir=false
DisableProgramGroupPage=true

[Code]

procedure DecodeVersion( verstr: String; var verint: array of Integer );
var
  i,p: Integer; s: string;
begin
  // initialize array
  verint := [0,0,0,0];
  i := 0;
  while ( (Length(verstr) > 0) and (i < 4) ) do
  begin
      p := pos('.', verstr);
      if p > 0 then
      begin
      if p = 1 then s:= '0' else s:= Copy( verstr, 1, p - 1 );
        verint[i] := StrToInt(s);
        i := i + 1;
        verstr := Copy( verstr, p+1, Length(verstr));
      end
      else
      begin
        verint[i] := StrToInt( verstr );
        verstr := '';
      end;
  end;

end;

// This function compares version string
// return -1 if ver1 < ver2
// return  0 if ver1 = ver2
// return  1 if ver1 > ver2
function CompareVersion( ver1, ver2: String ) : Integer;
var
  verint1, verint2: array of Integer;
  i: integer;
begin

  SetArrayLength( verint1, 4 );
  DecodeVersion( ver1, verint1 );

  SetArrayLength( verint2, 4 );
  DecodeVersion( ver2, verint2 );

  Result := 0; i := 0;
  while ( (Result = 0) and ( i < 4 ) ) do
  begin
      if verint1[i] > verint2[i] then
        Result := 1
      else
      if verint1[i] < verint2[i] then
          Result := -1
        else
          Result := 0;

      i := i + 1;
  end;

end;

// DirectX version is stored in registry as 4.majorversion.minorversion
// DirectX 8.0 is 4.8.0
// DirectX 8.1 is 4.8.1
// DirectX 9.0 is 4.9.0

function GetDirectXVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';

  RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\DirectX', 'Version', sVersion );

  Result := sVersion;
end;

function InitializeSetup(): Boolean;
begin
  // in this case program needs at least directx 8.0
  if CompareVersion( GetDirectXVersion(), '4.8.0') < 0 then
  begin
    Result := MsgBox('DirectX 8 not found. The 3d test will not run and games may not perform optimally. Do you wish to continue?',
        mbConfirmation, MB_YESNO) = idYes;
    if Result = False then
      MsgBox('Please upgrade your DirectX Version at http://www.microsoft.com/directx', mbInformation, MB_OK);
  end
  else
    Result := true;
end;


7 - PSVince is a DLL to detect if a module is loaded in memory

Article created: 27/05/02 19:15:57
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=7

PSVince is a DLL to detect if a module is loaded in memory. It's based on this MSKB article

MSKB 175030 How To Enumerate Applications Using Win32 APIs

Here you can download DLL, a sample how to use it and source code.

If don't need to install on Win9X or WinNT it is possible to use just code to detect if an exe is running How to detect if an application is running with code.
Essential code to use PSVince in Inno Setup

[Files]
Source: psvince.dll; Flags: dontcopy

[Code]

function IsModuleLoaded(modulename: String ):  Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall';

function InitializeSetup(): Boolean;
begin

  Result := Not IsModuleLoaded( 'msaccess.exe' );

end;


8 - How to detect if an application is running

Article created: 27/05/02 19:20:47
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=8

There are many ways to do that, looking for a mutex, a class, a window or a runnnig exe.

See also HOWTO: Detect instances running in any user session with AppMutex on Inno Setup Knowledge Base.
  • Look for a Mutex
  • Look for a Class Name
  • Look for a Window Name
  • Look for a exe runnig

Look for a Mutex

It is possible to use
function CheckForMutexes(Mutexes: String): Boolean;
to check for a specified mutex, to see which mutexes are created by an application you can use ProcessExplorer made by www.sysinternals.com

Look for a Class Name

It is possible to use
function FindWindowByClassName(const ClassName: String): Longint;
and get window handle if found. It is possible to use this handle to send window messages.

Look for a Window Name

Instaed of class it is possible to find a window if an application looking for windowname.
function FindWindowByWindowName(const WindowName: String): Longint;

Look for a exe runnig

Using PSVince it is possible detect if an exe is running.


9 - How to kill a process

Article created: 27/05/02 21:00:05
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=9

Originally this article showed how to kill a process using an utility from www.sysinternals.com.
It is not possible to use this utility,thanks to an anonymous reader, so here follow other possibilities:

1) It is possible to use JanH Killer

or

2) Use this small utility, provided by this anonymous contributor, since he wrote:

I'm releasing it into the public domain and retaining no copyright, with no license restrictions.

It will kill any process on NT4 or later, including processes running as the calling user, or if you are a local Administrator, any process on the system - including services running under the special LocalSystem account that normally can't be terminated.

You can download source and program.


10 - How to detect vbscript runtime wscript.exe

Article created: 28/05/02 23:00:58
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=10

To be able to execute vbscript scripts, it is mandatory that wscript.exe is present in windows dir.
This component it is very common on recent windows platform, version 5.0 of this runtime is present on Windows 98 and 98 SE operating system, also it is installed by Office 2000 and Internet Explorer 5.0.
Version 5.1 is present on Windows 2000 and Windows ME operating system, and it is installed by Office Xp and Internet Explorer 5.5.
Version 5.6 is present on Windows XP operating systems and it is installed by Internet Explorer 6.0.
If it is needed it can be installed by an installer downlodable from http://msdn.microsoft.com/library/default.asp?url=/downloads/list/webdev.asp

See also HOWTO: Deploy Windows Scr ipt Runtime (scrrun.dll) on Inno Setup Knowledge Base.

[Setup]
AppName=WScriptVersion
AppVerName=WScriptVersion
DefaultDirName=WScriptVersion
DisableStartupPrompt=true
Uninstallable=false

[Code]

function InitializeSetup(): Boolean;
begin
  MsgBox( 'Version of WScript.exe is (' + GetWScriptVersion() + ')',
      mbInformation, MB_OK );
  Result := false;
end;

function GetWScriptVersion(): String;
var  sVersion, fname:  String;
begin
  sVersion := '';
  if UsingWinNT
    then fname := ChangeDirConst('{sys}\wscript.exe')
    else fname := ChangeDirConst('{win}\wscript.exe');
  GetVersionNumbersString( fname , sVersion );
  Result := sVersion;
end;


11 - How to detect current display resolution and color depth

Article created: 29/05/02 21:18:26
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=11

This script is useful to detect current display resolution and color depth.

Author: Jernej Simoncic

[Setup]
AppName=DisplayResoltution
AppVerName=DisplayResoltution
DefaultDirName=DisplayResoltution
DisableStartupPrompt=true
Uninstallable=false

[Code]
//functions to get BPP & resolution

//
// prototype for ISX 3.X
//
function DeleteDC(hDC: Integer): Integer;
external 'DeleteDC@GDI32 stdcall';

function CreateDC(lpDriverName, lpDeviceName, lpOutput: String; lpInitData: Integer): Integer;
external 'CreateDCA@GDI32 stdcall';

function GetDeviceCaps(hDC, nIndex: Integer): Integer;
external 'GetDeviceCaps@GDI32 stdcall';

Const
    HORZRES = 8;    //horizontal resolution
    VERTRES = 10;   //vertical resolution
    BITSPIXEL = 12; //bits per pixel
    PLANES = 14;    //number of planes (color depth=bits_per_pixel*number_of_planes)

function InitializeSetup(): Boolean;
  var xres, yres, bpp, pl, tmp: Integer;
      hDC: Integer;
begin

    //get resolution & BPP
    hDC := CreateDC('DISPLAY', '', '', 0);
    pl := GetDeviceCaps(hDC, PLANES);
    bpp := GetDeviceCaps(hDC, BITSPIXEL);
    xres := GetDeviceCaps(hDC, HORZRES); //horizontal resolution
    yres := GetDeviceCaps(hDC, VERTRES); //vertical resolution
    tmp := DeleteDC(hDC);
    bpp := pl * bpp;   //color depth

      MsgBox( 'Current resolution is ' + IntToStr(xres) +
          'x' + IntToStr(yres) +
          ' and color depth is ' + IntToStr( bpp )
          , mbInformation, MB_OK );

    Result := false;
end;


12 - DATViewer

Article created: 29/05/02 21:30:18
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=12

DatViewer is a program to see in a more readeable way DAT files created by an installer made with Inno Setup.
Here you can download DatViewer 1.1 and sources.

You can see a sample of what DatViewer produce reading file unins000.dat of My Inno Setup Extensions 2.0.14.

DatViewer Version=1.0.0.0
ID=Inno Setup Uninstall Log (b)
Version=4
AppID=My Inno Setup Extensions
AppName=My Inno Setup Extensions
   ufAdminInstalled=true
   ufDontCheckRecCRCs=false
   ufModernStyle=true
Num. of Records=52
utStartInstall
   ENTERPRISE
   Vincenzo
   C:\Programmi\My Inno Setup Extensions
utMutexCheck
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Compil32.exe
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\ISCC.exe
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Iscmplr.dll
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Default.isl
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\DefaultIsx.isl
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\WizClassicImage.bmp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\WizModernImage.bmp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\WizModernImage2.bmp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\WizModernSmallImage.bmp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\WizModernSmallImage2.bmp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\SetupLdr.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Uninst.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Regsvr.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\SetupModern.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\SetupModernDebug.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\isetup.hlp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\isetup.cnt
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\isfaq.htm
utDeleteDirOrFiles
   C:\Programmi\My Inno Setup Extensions\Sample
      utDeleteDirOrFiles_IsDir
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\MyProg.exe
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\MyProg.hlp
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Readme.txt
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Sample1.ifs
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Sample1.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Sample2.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Sample3.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\ScriptDebug.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\ScriptDlg.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\ScriptDll.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\Sample\Setup.iss
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\SetupClassic.e32
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\whatsnew.htm
utDeleteFile
   C:\Programmi\My Inno Setup Extensions\license.txt
utDeleteDirOrFiles
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions
      utDeleteDirOrFiles_IsDir
      utDeleteDirOrFiles_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\Inno Setup with My Inno Setup Extensions.lnk
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\Inno Setup with My Inno Setup Extensions.pif
      utDeleteFile_CallChangeNotify
utDeleteDirOrFiles
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions
      utDeleteDirOrFiles_IsDir
      utDeleteDirOrFiles_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\My Inno Setup Extensions documentation.lnk
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\My Inno Setup Extensions documentation.pif
      utDeleteFile_CallChangeNotify
utDeleteDirOrFiles
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions
      utDeleteDirOrFiles_IsDir
      utDeleteDirOrFiles_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\My Inno Setup Extensions sample script.lnk
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Menu Avvio\Programmi\My Inno Setup Extensions\My Inno Setup Extensions sample script.pif
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Desktop\Inno Setup with My Inno Setup Extensions.lnk
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Desktop\Inno Setup with My Inno Setup Extensions.pif
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Application Data\Microsoft\Internet Explorer\Quick Launch\Inno Setup with My Inno Setup Extensions.lnk
      utDeleteFile_CallChangeNotify
utDeleteFile
   C:\WINDOWS\Application Data\Microsoft\Internet Explorer\Quick Launch\Inno Setup with My Inno Setup Extensions.pif
      utDeleteFile_CallChangeNotify
utRegDeleteKeyIfEmpty
   Software\Martijn Laan
utRegDeleteEntireKey
   Software\Martijn Laan\My Inno Setup Extensions
utRegDeleteEntireKey
   Software\Microsoft\Windows\CurrentVersion\Uninstall\My Inno Setup Extensions_is1
utEndInstall


13 - How to detect IIS version installed

Article created: 29/05/02 21:41:42
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=13

This script get, if it's installed, version of IIS Internet Information Server.

[Setup]
AppName=IISVersion
AppVerName=IISVersion
DefaultDirName=IISVersion
DisableStartupPrompt=true
Uninstallable=false

[Code]

function GetIISVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ChangeDirConst('{sys}\inetsrv\inetinfo.exe') , sVersion );
  Result := sVersion;
end;

function InitializeSetup(): Boolean;
begin
  MsgBox( 'Version of IIS is (' + GetIISVersion() + ')',
      mbInformation, MB_OK );
  Result := false;
end;


14 - How to verify the program is already installed

Article created: 04/06/02 18:56:09
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=14

This script verify that the program is currently installed, and return name of the folder in which the program is currently installed.
Function GetPathInstalled return path where the progam was installed, it checks first in HKLM and then under HKCU registry key. This sample shows if and where ISX is installed.

;
; ISX 3.0.6.2
;

[Setup]
AppName=PrevInst
AppVerName=PrevInst
DefaultDirName=PrevInst
DisableStartupPrompt=true
Uninstallable=false

[Code]

function GetPathInstalled( AppID: String ): String;
var
   sPrevPath: String;
begin
  sPrevPath := '';
  if not RegQueryStringValue( HKLM,
    'Software\Microsoft\Windows\CurrentVersion\Uninstall'+AppID+'_is1',
    'Inno Setup: App Path', sPrevpath) then
    RegQueryStringValue( HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall'+AppID+'_is1' ,
      'Inno Setup: App Path', sPrevpath);

  Result := sPrevPath;
end;

function InitializeSetup(): Boolean;
var
    sPrevPath: String;
    sPrevID: String;
begin
  sPrevID := 'My Inno Setup Extensions 3';
  sPrevPath := GetPathInstalled( sprevID );

  if ( Length(sPrevPath) > 0 ) then
    MsgBox( 'Application with ID ' + sPrevID + ' is alredy installed in ' +
       sPrevPath, mbInformation, MB_OK )
  else
    MsgBox( 'Application with ID ' + sPrevID + ' is not installed' ,
        mbInformation, MB_OK );

  Result := false;
end;


15 - How to replace a line in a text file

Article created: 04/06/02 23:26:38
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=15

Script Author: Stefan Bracke

This function change the contents of the "config.nt" file. But any other textfile will do as well. The "config.nt" file, is a file which is called when a DOS-box (or a DOS application) is opened on a NT system. This function is used this to prepare a system to run a Clipper application from a network share.
Example how to use the ReplaceLine function:
{ Adjust CONFIG.NT }
ReplaceLine(AddBackslash(GetSystemDir) + 'config.nt','files=','files=99');
See also : Replace a line in a text file with comment check

[Code]

///////////////////////////////////////////////////////////////////////////////
// Find given text in given file, and replace
// complete textline with given text.
// Created: March 27, 2002 by S.T.I.Bracke
// Revised: May 22, 2002 by S.T.I.Bracke
function ReplaceLine(strFilename, strFind, strNewLine: String): Boolean;
var
  strTemp : String;
  iLineCounter : Integer;
  a_strTextfile : TArrayOfString;

begin
  { Load textfile into string array }
  LoadStringsFromFile(strFilename, a_strTextfile);

  { Search trough all textlines for given text }
  // old line
  // for iLineCounter := 1 to High(a_strTextfile) do
  // new line corrected by Nikolaus Moll
  for iLineCounter := 0 to GetArrayLength(a_strTextfile)-1 do
    begin
      { Overwrite textline when text searched for is part of it }
      if (Pos(strFind, a_strTextfile[iLineCounter]) > 0) then
        a_strTextfile[iLineCounter] := strNewLine;
    end;

  { Save string array to textfile (overwrite, no append!) }
  SaveStringsToFile(strFilename, a_strTextfile, False);

  Result := True;

end;


16 - How to add a line in a text file

Article created: 04/06/02 23:30:30
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=16

Script Author: Stefan Bracke

This function change the contents of the "config.nt" file. But any other textfile will do as well. The "config.nt" file, is a file which is called when a DOS-box (or a DOS application) is opened on a NT system. This function is used this to prepare a system to run a Clipper application from a network share.
Example how to use the AddTextAfterLine function:
{ Adjust CONFIG.NT }
AddTextAfterLine(AddBackslash(GetSystemDir) + 'config.nt','files=99',
'c:\winnt\system32\command.com c:\ /e:1024 /p');

[Code]

///////////////////////////////////////////////////////////////////////////////
// Find given text in given file, and insert
// given textline after line with text found.
// Created: March 27, 2002 by S.T.I.Bracke
// Revised: May 22, 2002 by S.T.I.Bracke
function AddTextAfterLine(strFilename, strFind, strAddLine: String): Boolean;
var
  strTemp : String;
  iLineCounter : Integer;
  a_strTextfile : TArrayOfString;
  fAlreadyExist : Boolean;

begin
  { Load textfile into string array }
  LoadStringsFromFile(strFilename, a_strTextfile);

  { Loop trough all textlines to check if line to add already exists }
  // old line
  // for iLineCounter := 1 to High(a_strTextfile) do
  // new line corrected by Nikolaus Moll
  for iLineCounter := 0 to GetArrayLength(a_strTextfile)-1 do
    begin
      if (a_strTextfile[iLineCounter] = strAddLine) then
        fAlreadyExist := fAlreadyExist or True
    end;

  { Only execute when new line doesn't exist in textfile yet }
  if not fAlreadyExist then
    begin
      { Search trough all textlines for given text }
      // old line
      // for iLineCounter := 1 to High(a_strTextfile) do
      // new line corrected by Nikolaus Moll
      for iLineCounter := 0 to GetArrayLength(a_strTextfile)-1 do
        begin
          { Add new textline after textline with text searched for }
          if (Pos(strFind, a_strTextfile[iLineCounter]) > 0) then
            a_strTextfile[iLineCounter] := a_strTextfile[iLineCounter] + #13 + #10 + strAddLine;
        end;

      { Save string array to textfile (overwrite, no append!) }
      SaveStringsToFile(strFilename, a_strTextfile, False);
    end;

  Result := True;

end;


17 - How to detect IE version installed (2)

Article created: 07/06/02 19:20:31
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=17

Script Author: Matthias Mohr

This script is useful to detect if, and which version of IE Internet Explorer is present on the machine.
This script detect also version of IE minor than 4, in this sample also program detect if version of Internet Explorer is ok, if not show a message and do not procede with setup.

;ISX 2.0.18
[Setup]
AppName=IEVersion2
AppVerName=IEVersion2
DefaultDirName=IEVersion2
DisableStartupPrompt=true
Uninstallable=false

[Code]
function InitializeSetup( ): Boolean;
var
  sIEVersion:  String;
  cIEMinVersionRegistry: String;
  cIEMinVersionShowed: String;

begin
  Result := true;

  // Initialize constants:
  cIEMinVersionRegistry := '4.72';  // this is display version v4.01
  cIEMinVersionShowed   := '4.01SP2'; // this is registry entry v4.72.xxx

  // now we check the version of the installed Internet Explorer:
  sIEVersion := GetIExplorerVersion();
  if ( sIEVersion <= cIEMinVersionRegistry ) then
  begin
    if ( sIEVersion = '' ) then
      sIEVersion := '';
    MsgBox( 'ERROR: The Program needs an installed' + #13 + #10 +
            '       Microsoft Internet Explorer >= v' + cIEMinVersionShowed + #13 + #10 +
            '       (detected version:"' + sIEVersion + '") !' + #13 + #10 + #13 + #10 +
            'Please install the necessary version (or higher) and then reenter the setup !',
            mbError, MB_OK );
    Result := false;
  end;
end; // END_FUNC InitializeSetup

function GetIExplorerVersion(): String;
var
  sVersion:  String;
begin
  RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Internet Explorer', 'Version', Result );
  if ( Result = '' ) then
  begin
    // the registry-key does not exist;
    //   versions before v4 used another one --> check this also
    RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Internet Explorer', 'IVer', sVersion );
    if ( sVersion <> '' ) then
    begin
      Result := '';
      case sVersion of
        '103':
          Result := '3.0';
        '102':
          Result := '2.5';
        '101':
          Result := '2.0';
        '100':
          Result := '1.0';
      end; // END case sVersion of
    end; // END if ( sVersion <> '' ) then
  end; // END if ( Result = '' ) then
end; // END_FUNC GetIEXplorerVersion


18 - How to detect ADSI version installed

Article created: 10/06/02 20:50:09
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=18

This script shows which version of ADSI is installed on the computer, this is useful to prevent to run a script that needs ADSI services.
Thi check is mandatory only for old OS, because ADSI is already installed on Windows 2000 and Windows Xp, on Win9X and WinNT are available from MS packages that install it.
This script is based on MS article INFO: Determining Which Version of ADSI Is Installed (Q216290)

; ISX 2.0.18
[Setup]
AppName=ADSIVersion
AppVerName=ADSIVersion
DefaultDirName=ADSIVersion
DisableStartupPrompt=true
Uninstallable=false

[Code]

function InitializeSetup(): Boolean;
var adsiver: String;
begin
  adsiver := GetADSIVersion();

  if Length(adsiver) > 0 then begin
      MsgBox( 'Version of ADSI is (' + adsiver + ')', mbInformation, MB_OK );
      Result := true;
  end
  else begin
      MsgBox( 'ADSI is not presnet', mbInformation, MB_OK );
      Result := false;
  end;
end;

function GetADSIVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  GetVersionNumbersString( ChangeDirConst('{sys}\adsnt.dll') , sVersion );
  Result := sVersion;
end;


19 - How to get drive type

Article created: 26/06/02 20:40:26
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=19

This script shows how to get drive type of a drive, in this sample show disk type from where the setup was launched.

;
; ISX 3.0.6.2
;

[Setup]
AppName=DriveType
AppVerName=DriveType
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\Drvtype
DisableStartupPrompt=true

[Code]

function GetDriveType( lpDisk: String ): Integer;
external 'GetDriveTypeA@kernel32.dll stdcall';

const
  DRIVE_UNKNOWN = 0; // The drive type cannot be determined.
  DRIVE_NO_ROOT_DIR = 1; // The root path is invalid. For example, no volume is mounted at the path.
  DRIVE_REMOVABLE = 2; // The disk can be removed from the drive.
  DRIVE_FIXED = 3; // The disk cannot be removed from the drive.
  DRIVE_REMOTE = 4; // The drive is a remote (network) drive.
  DRIVE_CDROM = 5; // The drive is a CD-ROM drive.
  DRIVE_RAMDISK = 6; // The drive is a RAM disk.

// function to convert disk type to string
function DriveTypeString( dtype: Integer ): String;
begin
  case dtype of
    DRIVE_NO_ROOT_DIR : Result := 'Root path invalid';
    DRIVE_REMOVABLE : Result := 'Removable';
    DRIVE_FIXED : Result := 'Fixed';
    DRIVE_REMOTE : Result := 'Network';
    DRIVE_CDROM : Result := 'CD-ROM';
    DRIVE_RAMDISK : Result := 'Ram disk';
  else
    Result := 'Unknown';
  end;

end;

function InitializeSetup(): Boolean;

var srcdisk: String;
    disktype: Integer;

begin
  Result := true;

  // get drive from where setup was launched
  srcdisk := AddBackSlash( ExtractFileDrive( ExpandConstant('{srcexe}') ) ) ;

  // get number type of disk
  disktype := GetDriveType( srcdisk );

  // show which type of disk it is
  MsgBox( Format2( 'Setup lanuched from %s of type (%s)', srcdisk, DriveTypeString( disktype ) ) ,
     mbInformation, MB_OK );

end;


20 - How to get drive volume and serial number

Article created: 26/06/02 20:47:49
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=20

This script shows how to get drive volume name and serial number, in this sample show disk information from where the setup was launched. In this sample there is also a call to function GetLastError to show an error message if function GetVolumeInformation return error.

;
; ISX 3.0.6.2
;

[Setup]
AppName=DriveVolume
AppVerName=DriveVolume
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\DriveVolume
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

function GetVolumeInformation(
    lpRootPathName: PChar;
    lpVolumeNameBuffer: PChar;
    nVolumeNameSize: LongInt;
    var lpVolumeSerialNumber: LongInt;
    lpMaximumComponentLength: LongInt;
    lpFileSystemFlags : LongInt;
    lpFileSystemNameBuffer: PChar;
    nFileSystemNameSize: LongInt ) : Integer;
external 'GetVolumeInformationA@kernel32.dll stdcall';

{ // API declaration in C
BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,           // root directory
  LPTSTR lpVolumeNameBuffer,        // volume name buffer
  DWORD nVolumeNameSize,            // length of name buffer
  LPDWORD lpVolumeSerialNumber,     // volume serial number
  LPDWORD lpMaximumComponentLength, // maximum file name length
  LPDWORD lpFileSystemFlags,        // file system options
  LPTSTR lpFileSystemNameBuffer,    // file system name buffer
  DWORD nFileSystemNameSize         // length of file system name buffer
);
}

function GetLastError( ) : Integer;
external 'GetLastError@kernel32.dll stdcall';

function InitializeSetup(): Boolean;

var srcdisk, volume: String;
    ercode: Integer; sernum: LongInt;
begin
  Result := true;

  srcdisk := AddBackslash( ExtractFileDrive( ExpandConstant('{srcexe}') ) );
  volume := StringOfChar( ' ', 16 );

  if GetVolumeInformation( srcdisk, volume, 15, sernum, 0, 0, '', 0 ) = 0 then
    begin
      ercode := GetLastError();
      MsgBox( SysErrorMessage( ercode ), mbError, MB_OK );
      Result := false;
    end
  else begin
    volume := CastIntegerToString( CastStringToInteger(volume) );
    MsgBox( Format3( 'Volume of %s is (%s) serial is %s', srcdisk, volume, IntToStr(sernum) )
      , mbInformation, MB_OK );
  end;
end;


21 - How to detect if and which version of MS Access is installed

Article created: 19/07/02 21:45:17
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=21

This script shows wow to detect if and which version of MS Access is installed, to detect it program read this registry key that in default value has path of msaccess.exe HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\MSACCESS.EXE. Another way to detect MS Access and it's version installed is this.

;
; ISX 3.0.2
;
;
[Setup]
AppName=MSAccessVersion
AppVerName=MSAccessVersion
DefaultDirName={pf}\MSAccessVersion
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=MSAccessVersion

[Code]

// Legenda
// 8.00.00.3512   Access 97
// 8.00.00.4122   Access 97 SR1
// 8.00.00.5903   Access 97 SR2
// 9.00.00.2719   Access 2000
// 9.00.00.3822   Access 2000 SR1
// 9.00.00.4506   Access 2000 SR2
// 10.00.2627.1   Access XP

function AccessVersion(): String;
var accversion, accpath: String;
begin
  accversion := '';

  if RegQueryStringValue(HKEY_LOCAL_MACHINE,
       'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\MSACCESS.EXE',
       '', accpath) then
  begin
    // in varResult there is path of msaccess.exe
    GetVersionNumbersString( accpath, accversion );
  end;

  Result := accversion;
end;

function InitializeSetup(): Boolean;
var accver: String;
begin
  accver := AccessVersion();
  if Length( accver ) > 0 then
  begin

      MsgBox( 'Version ('+ accver +') of MS Access is installed.',
        mbInformation, MB_OK );

    // go on with setup
    Result := true;

  end
  else begin
      MsgBox( 'MS Access is not installed.', mbInformation, MB_OK );

      // cannot go with setup anyway
      Result := false;
  end;

end;


22 - How to detect if and which version of SQL Server or MSDE is installed

Article created: 19/07/02 21:50:38
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=22

This script shows how to detect if and which version of SQL Server or MSDE is installed, to detect it script read this registry key

HKLM\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion\CurrentVersion

This registry key can be accessed only by Admin users, so I check IsAdminLoggedOn before read it and also I added AdminPrivilegesRequired=true in [Setup] section.

;
; ISX 3.0.2
;
[Setup]
AppName=SQL_MSDEVersion
AppVerName=SQL_MSDEVersion
DefaultDirName={pf}\SQL_MSDEVersion
DisableStartupPrompt=true
Uninstallable=false
OutputBaseFilename=SQL_MSDEVersion
DisableDirPage=true
AdminPrivilegesRequired=true

[Code]

function GetSQLMSDEVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  // it is necessary to be an Admin to read this registry key
  if IsAdminLoggedOn then
      RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion', 'CurrentVersion', sVersion );

  Result := sVersion;
end;

function InitializeSetup(): Boolean;
var sqlver: String;
begin
  sqlver := GetSQLMSDEVersion();
  if Length( sqlver ) > 0 then
  begin

      MsgBox( 'Version ('+ sqlver +') of SQL Server/MSDE is installed.',
        mbInformation, MB_OK );

    // go on with setup
    Result := true;

  end
  else begin
      MsgBox( 'SQL Server/MSDE is not installed.', mbInformation, MB_OK );

      // cannot go with setup anyway
      Result := false;
  end;

end;


23 - Sample MSDE 2.0 install script

Article created: 22/07/02 20:23:53
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=23

Script Author: Lauchlan M.

The script works but is not great, as when it runs the setup.exe in the [Run] section setup.exe kicks off the windows installer taking it out of the Innosetup installation control loop so that you can't have innosetup doing things afterwards like doing the reboot in a controlled way.

A better solution seems to be to do the installation (run the MSDE setup.exe) from a Delphi exe, as then you can check for previous installs of MSDE / SQL Server and conflicts with your instancename as you go. This exe would be run from innosetup. It is a more complicated solution, and doesn't solve the problem of the Windows Installer stealing control from your setup.

Any suggestions on better MSDE installs appreciated: email lmackinnon(at)hotmail.com.

[Files]

; APPLICATION EXECUTABLE:
Source: MyApplicationPath\myProject.exe; DestDir: {app}; CopyMode: alwaysoverwrite

;DATABASE FILES AND CONNECTION FILE:
Source: MyApplicationPath\MyDatabase.MDF; DestDir: {app}
Source: MyApplicationPath\MyDatabase.LDF; DestDir: {app}

; MSDE SETUP FILES:
Source: MyApplicationPath\MSDESetup\Setup.exe; DestDir: {app}\msde
Source: MyApplicationPath\MSDESetup\Setup.rll; DestDir: {app}\msde
Source: MyApplicationPath\MSDESetup\SQLRESLD.dll; DestDir: {app}\msde
Source: MyApplicationPath\MSDESetup\setup\SqlRun01.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun02.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun03.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun04.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun05.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun06.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun07.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun08.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun09.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun10.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun11.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun12.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun13.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun14.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun15.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun16.msi; DestDir: {app}\msde\setup
Source: MyApplicationPath\MSDESetup\setup\SqlRun.cab; DestDir: {app}\msde\setup
;Source: MyApplicationPath\MSDESetup\MSDESetup.exe; DestDir: {app}\msde

[Run]

; MSDE SETUP
Filename: "{app}\msde\Setup.exe"; Parameters: "SECURITYMODE=SQL instancename=""MyInstanceName"""; WorkingDir: {app}\msde; StatusMsg: Installing Microsoft Data Engine

;PROGRAM EXECUTABLE - doesn't get run because Windows Installer steals control -
Filename: {app}\MyProject.exe; Flags: nowait postinstall skipifsilent


24 - How to create a ODBC DSN

Article created: 23/07/02 20:17:19
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=24

A DSN ODBC on a PC are registry keys under SOFTWARE\ODBC\ODBC.INI, in HKLM for System DSN and in HKCU for User DSN. This MS KB article it's a sample on how to create a Data Source Name with Visual Basic.

Q184608 - HOWTO Programmatically Create a DSN for SQL Server with VB
If you want to create a DSN in your setup program with InnoSetup it's enough to include in the section [Registry] all registry key to create the DSN. It's a task not very easy but to avoid errors and problems you can follow these steps to get registry keys that you need, with help of ISTool.

  1. Create on your PC a DSN as you wish setup program ahe to create, Name and all settings
    2. Start ISTool and from menu Project select Create ODBC Profile... or in Registry section rigth-click and select Create ODBC Profile...
  2. A window will show all DSN on the PC, select the DSN previously created
  3. In the Registry section of the script will appear all registry setting of the DSN
  4. Change all path of the PC accordigly with InnoSetup constants as {app} or {sys}
  5. cut and paste this section in your InnoSetup script

The code below is a sample of importing a System DSN named ISXKBDB that refer a MS Access DB, after the import it's necessary to change the path of Driver, that from C:\WINDOWS\SYSTEM\ODBCJT32.DLL to {sys}\ODBCJT32.DLL.
Same change for path of DB that from c:\Documenti\db\mydb.mdb change to {app}\db\mydb.mdb because the DB is installed in {app}\db.

[Files]
Source: C:\Documenti\db\mydb.mdb; DestDir: {app}\db

;
; Just Imported
;
[Registry]
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ODBC Data Sources; ValueType: string; ValueName: ISXKBDB; ValueData: Microsoft Access Driver (*.mdb); Flags: createvalueifdoesntexist uninsdeletevalue
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Driver; ValueType: string; ValueData: C:\WINDOWS\SYSTEM\ODBCJT32.DLL
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: DBQ; ValueType: string; ValueData: c:\Documenti\db\mydb.mdb
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Description; ValueType: string; ValueData: ISX Knowledge Base Database
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: DriverId; ValueType: dword; ValueData: 25
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: FIL; ValueType: string; ValueData: MS Access
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: SafeTransactions; ValueType: dword; ValueData: 0
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: UID; ValueType: string; ValueData:
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: ImplicitCommitSync; ValueType: string; ValueData:
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: MaxBufferSize; ValueType: dword; ValueData: 2048
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: PageTimeout; ValueType: dword; ValueData: 5
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Threads; ValueType: dword; ValueData: 3
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: UserCommitSync; ValueType: string; ValueData: Yes

;
; After changes
;
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ODBC Data Sources; ValueType: string; ValueName: ISXKBDB; ValueData: Microsoft Access Driver (*.mdb); Flags: createvalueifdoesntexist uninsdeletevalue
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Driver; ValueType: string; ValueData: {sys}\ODBCJT32.DLL
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: DBQ; ValueType: string; ValueData: {app}\db\mydb.mdb
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Description; ValueType: string; ValueData: ISX Knowledge Base Database
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: DriverId; ValueType: dword; ValueData: 25
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: FIL; ValueType: string; ValueData: MS Access
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: SafeTransactions; ValueType: dword; ValueData: 0
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: UID; ValueType: string; ValueData:
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: ImplicitCommitSync; ValueType: string; ValueData:
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: MaxBufferSize; ValueType: dword; ValueData: 2048
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: PageTimeout; ValueType: dword; ValueData: 5
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: Threads; ValueType: dword; ValueData: 3
Root: HKLM; SubKey: Software\ODBC\ODBC.INI\ISXKBDB\Engines\Jet; Flags: createvalueifdoesntexist uninsdeletevalue; ValueName: UserCommitSync; ValueType: string; ValueData: Yes


25 - Additional registry functions

Article created: 10/09/02 23:18:46
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=25

Ton Huisman, Aug 2002 ath(at)ath.myweb.nl wrote some useful registry function freeware, no claims, no f(l)ames, use at your own risk, test before releasing to public!!
RegKeyExists: Query for the existence of some Keyvalue in the registry
RegOpenKey: Open a Keyvalue in the registry
RegKeyNames: Query for a string of all SubKeys of a SubKey in the registry

[Code]
// External functions:
// InnoSetup 2 versions:
//function RegOpenKeyEx(RootKey : Integer;subkey : string;options: integer;sec:integer;valu:integer): integer; external 'advapi32' name 'RegOpenKeyExA'; stdcall;
//function RegCloseKey(valu:integer): integer; external 'advapi32' name 'RegCloseKey'; stdcall;
//function RegEnumKeyEx(hKey: Integer;dwIndex: Integer; lpName : Pchar; lpcbName: PChar;lpReserved: Integer;lpClass: Pchar;lpcbClass: Integer;lpFTLWT: Integer): Integer; external 'advapi32' name 'RegEnumKeyExA'; stdcall;
// InnoSetup 3 versions:
function RegOpenKeyEx(RootKey : Integer;SubKey: String;Options: Integer;Sec: Integer; Valu: Integer): Integer; external 'RegOpenKeyExA@advapi32.dll stdcall';
function RegCloseKey(Valu: Integer): Integer; External 'RegCloseKey@advapi32.dll stdcall';
function RegEnumKeyEx(hKey: Integer;dwIndex: Integer; lpName : Pchar; lpcbName: PChar;lpReserved: Integer;lpClass: Pchar;lpcbClass: Integer;lpFTLWT: Integer): Integer; external 'RegEnumKeyExA@advapi32.dll stdcall';
// End external functions

// From winerror.h/windows.pas:
Const KEY_QUERY_VALUE = $1;
Const KEY_ENUMERATE_SUB_KEYS = $8;
Const ERROR_SUCCESS = 0;
Const ERROR_MORE_DATA = 234;
Const ERROR_NO_MORE_ITEMS = 259;

//**********************************************************
// What this is all about:
// Created by Ton Huisman, Aug 2002 ath@ath.myweb.nl
// Freeware, no claims, no f(l)ames, use at your own risk, test before releasing to public!!
//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyExists: Query for the existence of some Keyvalue in the registry
// -> Function RegKeyExists(RootKey : Integer; SubKey : String) : Boolean;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
// Output: Boolean: True if found, False if not found.
// Queried by trying to open the key for read access, True if successful
// Uses: RegOpenKeyEx, CastPtrToInt, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
// RegOpenKey: Open a Keyvalue in the registry
// -> Function RegOpenKey(RootKey : Integer; SubKey : String;Sam: Integer) : Integer;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
//		   Sam	   : The Security Acces Mask, If 0 then KEY_QUERY_VALUE
// Output: Integer (HKey): Handle for registry key, 0 if not Ok
// Uses: RegOpenKeyEx
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyNames: Query for a string of all SubKeys of a SubKey in the registry
// -> Function RegKeyNames(RootKey: Integer; SubKey : String) : String;
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
// Output: String: Comma-separated list of all subkeys found
// Uses: RegOpenKey, CastIntToPtrStr, RegEnumKeyEx, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////

//**********************************************************
// Supporting function
Function CastPtrToInt(PtrStr: String) : Integer;
Begin
    Result := Ord(StrGet(PtrStr,1)) + (Ord(StrGet(PtrStr,2)) * 256) + (Ord(StrGet(PtrStr,3))*256*256) + (Ord(StrGet(PtrStr,4))*256*256*256);
End; { CastPtrToInt }

// Supporting function
// Cast Integer to PointerString
Function CastIntToPtrStr(Inte: Cardinal) : String;
Var n : Integer;
    r : Cardinal;
    t : Cardinal;
Begin
    Result := StringOfChar(#0,4);
    r := Inte;
    n := 4;
    If r >= (256*256*256) then
    Begin
        t := r / (256*256*256);
        StrSet(Chr(t),n,Result);
        r := r - (t * (256*256*256));
    End;
    n := n - 1;
    If r >= (256*256) then
    Begin
        t := r / (256*256);
        StrSet(Chr(t),n,Result);
        r := r - (t * (256*256));
    End;
    n := n - 1;
    If r >= (256) then
    Begin
        t := r / (256);
        StrSet(Chr(t),n,Result);
        r := r - (t * (256));
    End;
    n := n - 1;
    If r > 0 then
    Begin
        t := r;
        StrSet(Chr(t),n,Result);
        r := r - t;
    End;
End; { CastIntToPtrStr }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyExists: Query for the existence of some Keyvalue in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
// Output: Boolean: True if found, False if not found.
// Queried by trying to open the key for read access, True if successful
// Uses: RegOpenKeyEx, CastPtrToInt, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////
Function RegKeyExists(RootKey : Integer; SubKey : String) : Boolean;
Var l : Integer;
    ls : String;
    hKey : Integer;
Begin
    ls := StringOfChar(#0,4);
    l := CastStringToInteger(ls);
    Result := (RegOpenKeyEx(RootKey,SubKey,0,KEY_QUERY_VALUE,l) = 0);
    hKey := CastPtrToInt(ls);
    RegCloseKey(hKey);
End; { RegKeyExists }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegOpenKey: Open a Keyvalue in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
//		   Sam	   : The Security Acces Mask, If 0 then KEY_QUERY_VALUE
// Output: Integer (HKey): Handle for registry key, 0 if not Ok
// Uses: RegOpenKeyEx
//////////////////////////////////////////////////////////////////////////////////////////////
Function RegOpenKey(RootKey : Integer; SubKey : String;Sam: Integer) : Integer;
Var l : Integer;
    ls : String;
Begin
    ls := StringOfChar(#0,4);
    l := CastStringToInteger(ls);
    If Sam = 0 then Sam := KEY_QUERY_VALUE;
    Result := 0;
    If (RegOpenKeyEx(RootKey,SubKey,0,Sam,l) = 0) then
        Result := CastPtrToInt(ls);
End; { RegOpenKey }

//////////////////////////////////////////////////////////////////////////////////////////////
// RegKeyNames: Query for a string of all SubKeys of a SubKey in the registry
// Input : RootKey : A predefined value for the registryhive we want to query, like HKLM etc.
//		   SubKey  : The full path to the queried key
// Output: String: Comma-separated list of all subkeys found
// Uses: RegOpenKey, CastIntToPtrStr, RegEnumKeyEx, RegCloseKey
//////////////////////////////////////////////////////////////////////////////////////////////

Const Key_Separator = ',';	// Default to comma, change as needed, could be #9 or ';'?
Const Key_MaxLength = 255;	// Usually this (255) is way to large, 64 could do niceley, but just be safe. Longer keys than this length will be ignored!

Function RegKeyNames(RootKey: Integer; SubKey : String) : String;
Var hKey : Integer;	// HKey is not directly supported...
    wdIndex : Integer;
    s : String;
    ss : Integer;
    st : String;
    r : Integer;
Begin
    wdIndex := 0;
    Result := '';
    s := StringOfChar(#0,Key_MaxLength);
    ss := Key_MaxLength;
    st := CastIntToPtrStr(ss);
    hKey := RegOpenKey(RootKey,SubKey,KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS);
    If hKey <> 0 then
    Begin
        r := RegEnumKeyEx(hKey,wdIndex,s,st,0,'',0,0);
        While ((r = ERROR_SUCCESS) or (r = ERROR_MORE_DATA)) do
        Begin
            ss := CastPtrToInt(st);
            If ss > 0 then
                Result := Result + Copy(s,1,ss) + Key_Separator;
            wdIndex := wdIndex + 1;
            st := CastIntToPtrStr(ss);
            r := RegEnumKeyEx(hKey,wdIndex,s,st,0,'',0,0);
        End;
        If Result <> '' then
            Result := Copy(Result,1,Length(Result) - 1);// remove extra separator ;-)
        RegCloseKey(hKey);
    End;
End; { RegKeyNames }


26 - How to detect Phisical memory

Article created: 10/09/02 23:18:06
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=26

This sample show how to get memory data, this sample uses function GlobalMemoryStatus and return structure MEMORYSTATUS filled with data.
It is possible to check if PC has enough memory to run properly software that is installing.

;
; ISX 3.0.2
;
; Setup can continue if available physical memory is bigger than 32
;
[Setup]
AppName=PhysMem
AppVerName=PhysMem
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\PhysMem
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

type
  TMemoryStatus = record
    dwLength : Longint;
    dwMemoryLoad : Longint;
    dwTotalPhys : Longint;
    dwAvailPhys : Longint;
    dwTotalPageFile : Longint;
    dwAvailPageFile : Longint;
    dwTotalVirtual : Longint;
    dwAvailVirtual : Longint;
  end;

procedure GlobalMemoryStatus(var lpBuffer: TMemoryStatus); external
'GlobalMemoryStatus@kernel32.dll';

function InitializeSetup: Boolean;
var
  M: TMemoryStatus;
  s, crlf: string;
  oneMB: Longint;
begin

  crlf := #13#10;
  oneMB := 1024 * 1024;

  GlobalMemoryStatus(M);

  s := 'Phisical Memory is : ' + IntToStr( M.dwTotalPhys / oneMB ) + ' MByte' +
  crlf + 'Available Memory is : ' + IntToStr( M.dwAvailPhys / oneMB ) + ' MByte';

  MsgBox( s ,mbInformation, MB_OK );

  // it's ok if Availanle Phisical memory is bigger than 32 MB
  Result := ( M.dwAvailPhys / oneMB ) >= 32;

  if ( not Result ) then
    MsgBox( 'Not enough memory' , mbError, MB_OK );

end;


27 - How to detect filesystem type

Article created: 17/09/02 23:00:11
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=27

This sample shows hot to detect filesystem type of a disk. User select folder where install program, on NextButtonclick code check filesystem type of select folder.
If user select a NTFS drive setup can continue, if user select a folder with a filesystem as FAT32 or other than NTFS it stops.

;
;
; ISX 3.0.2

[Setup]
AppName=Filesystem
AppVerName=Filesystem
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\Filesystem
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]
function GetVolumeInformation(
  lpRootPathName: PChar;
  lpVolumeNameBuffer: PChar;
  nVolumeNameSize: LongInt;
  var lpVolumeSerialNumber: LongInt;
  lpMaximumComponentLength: LongInt;
  lpFileSystemFlags : LongInt;
  lpFileSystemNameBuffer: PChar;
  nFileSystemNameSize: LongInt ) : Integer;
external 'GetVolumeInformationA@kernel32.dll';

{ // API declaration in C
BOOL GetVolumeInformation(
  LPCTSTR lpRootPathName,           // root directory
  LPTSTR lpVolumeNameBuffer,        // volume name buffer
  DWORD nVolumeNameSize,            // length of name buffer
  LPDWORD lpVolumeSerialNumber,     // volume serial number
  LPDWORD lpMaximumComponentLength, // maximum file name length
  LPDWORD lpFileSystemFlags,        // file system options
  LPTSTR lpFileSystemNameBuffer,    // file system name buffer
  DWORD nFileSystemNameSize         // length of file system name buffer
);
}

function GetLastError( ) : Integer; external 'GetLastError@kernel32.dll';

function NextButtonClick(CurPage: Integer): Boolean;
var srcdisk : String;
    ercode: Integer; sernum: LongInt;
    fstype: String;
begin

  // by default go to next page
  Result := true;

  // if curpage is wpSelectDir check is filesystem
  if CurPage = wpSelectDir then
  begin

    srcdisk := AddBackslash( ExtractFileDrive( WizardDirValue ) );
    fstype := StringOfChar( ' ', 12 );

    if GetVolumeInformation( srcdisk, '', 0, sernum, 0, 0, fstype, 11 ) = 0 then
    begin
      ercode := GetLastError();
      MsgBox( SysErrorMessage( ercode ), mbError, MB_OK );
      Result := false;
    end
    else begin
      fstype := CastIntegerToString( CastStringToInteger(fstype) );

      MsgBox( Format2( 'Volume %s has filesystem type (%s)', srcdisk, fstype )
        , mbInformation, MB_OK );

      // go on only if filesystem is NTFS
      Result := ( Uppercase(Trim(fstype)) = 'NTFS' );

    end;

  end;

end;


28 - How to logoff if user is not Administrator

Article created: 17/09/02 23:04:34
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=28

This sample check is user is Administrator, if not, setup cannot continue, anyway installer asks if user wants to logoff, if so it do it.
Logoff is done with API ExitWindowsEx.

;
;
; ISX 3.0.2
;

[Setup]
AppName=LogOff
AppVerName=LogOff
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\LogOff
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]
function ExitWindowsEx(
    uFlags: Longint;
    dwReserved: LongInt ) : Integer;
external 'ExitWindowsEx@user32.dll stdcall';

{ // API declaration in C
BOOL ExitWindowsEx(
  UINT uFlags,       // shutdown operation
  DWORD dwReserved   // reserved
);
}

// from winuser.h
const EWX_LOGOFF = 0;
const EWX_SHUTDOWN = 1;
const EWX_REBOOT = 2;
const EWX_FORCE = 4;
const EWX_POWEROFF = 8;

function GetLastError( ) : Integer;
external 'GetLastError@kernel32.dll';

function InitializeSetup(): Boolean;
var answ: Integer; CrLf: String;
    ercode: Integer;
begin
  Result := true;

  CrLf := #13#10;

  // if is nt alogged on an Admin ask if wants logoff
  if not IsAdminLoggedOn then
  begin
    // anyway cannot continue setup
    Result := false;

    answ := MsgBox( 'User logged on is not an Administrator, please logoff and logon as administrator.' +
      CrLf + 'Do you want lo logoff now ?', mbConfirmation, MB_YESNO );

    if answ = IDYES then
    begin
      answ := ExitWindowsEx( EWX_LOGOFF, 0 );
      // if answ is 0 Exitwindows had erros so show error message
      if answ = 0 then
      begin
        ercode := GetLastError();
        MsgBox( 'Logoff error' + CrLf +
          IntToStr(ercode) + ':' + SysErrorMessage( ercode ), mbError, MB_OK );
      end;
    end;
  end;

end;


29 - How to prompt for an additional directory for data

Article created: 26/09/02 21:12:40
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=29

This scipt shows how to insert a custom wizard page after the directory page, and prompt for another directory where to install some other files, for example a database.
This script is a small version of a more complex script that shows also many things about custom wizard pages, that script is ScriptDlg.iss that is installed in Sample directory of your My InnoSetup Extensions directory.

Inno Setup 5.x version of this script is How to prompt for an additional folder for data for IS 5

;
; ISX 3.0.2
;

[Setup]
AppName=SampleProg
AppVerName=SampleProg
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\SampleProg
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Files]
;Main program that will be installed in {app} folder
Source: SampleProg.exe; DestDir: {app}

;Database file that will installed where user choosed
Source: DataBase.mdb; DestDir: {code:GetDataDir|''}

[Code]
var
// variable that store data directory
  DataDir: String;

function PromptDataDir( BackClicked: Boolean): Boolean;
var
  Next: Boolean;
begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('Select Database Directory');
    ScriptDlgPageSetSubCaption1('Where should database files be installed?');
    ScriptDlgPageSetSubCaption2('Select the folder you would like Setup to install database files to, then click Next.');

    // Initialize the DataDir, append \Data to folder choosed for {app}
    DataDir := WizardDirValue + '\Data';

    // Ask for a dir until the user has entered one or click Back or Cancel
    Next := InputDir( 'Data', DataDir);

    while Next and (DataDir = '') do begin
      MsgBox(SetupMessage(msgInvalidPath), mbError, MB_OK);
      Next := InputDir('Data', DataDir);
    end;

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpSelectDir)) or (BackClicked and (CurPage = wpReady)) then begin
    // Insert a custom wizard page between two non custom pages
    Result := PromptDataDir( BackClicked );
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;

function GetDataDir(S: String): String;
begin
  // Return the selected DataDir
  Result := DataDir;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,

 MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  // Fill the 'Ready Memo' with the normal settings and the custom settings
  S := '';

  S := S + MemoDirInfo + NewLine + NewLine;

  S := S + 'Database path' + NewLine;
  S := S + Space + DataDir + NewLine;

  Result := S;
end;


30 - How to detect first CD-ROM drive in the system

Article created: 02/10/02 0:44:53
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=30

This script shows how to return first CD-ROM in the system. To get all drives it is necessary to call GetLogicalDriveStrings API.
For every drive letter script check drive type and stops at first CD-ROM, setup in this case doesn't continue if there isn't any CD-ROM.

;
; ISX 3.0.2
;

[Setup]
AppName=FirstCD
AppVerName=FirstCD
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\FirstCD
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

function GetDriveType( lpDisk: String ): Integer;
external 'GetDriveTypeA@kernel32.dll stdcall';

function GetLogicalDriveStrings( nLenDrives: LongInt; lpDrives: String ): Integer;
external 'GetLogicalDriveStringsA@kernel32.dll stdcall';

const
  DRIVE_UNKNOWN = 0; // The drive type cannot be determined.
  DRIVE_NO_ROOT_DIR = 1; // The root path is invalid. For example, no volume is mounted at the path.
  DRIVE_REMOVABLE = 2; // The disk can be removed from the drive.
  DRIVE_FIXED = 3; // The disk cannot be removed from the drive.
  DRIVE_REMOTE = 4; // The drive is a remote (network) drive.
  DRIVE_CDROM = 5; // The drive is a CD-ROM drive.
  DRIVE_RAMDISK = 6; // The drive is a RAM disk.

function GetFirstCD(): String;
var disktype, posnull: Integer;
    drivesletters: String; lenletters: Integer;
    drive: String; found : Boolean;
begin
  //get all drives letters of system
  drivesletters := StringOfChar( ' ', 64 );
  lenletters := GetLogicalDriveStrings( 63, drivesletters );
  SetLength( drivesletters , lenletters );

  found := false; drive := '';

  while ( (Length(drivesletters) > 0) and not found ) do
  begin
    posnull := Pos( #0, drivesletters );
      if posnull > 0 then
      begin
      drive:= Copy( drivesletters, 1, posnull - 1 );

      // get number type of disk
      disktype := GetDriveType( drive );

      found := ( disktype = DRIVE_CDROM );

        drivesletters := Copy( drivesletters, posnull+1, Length(drivesletters));
      end
  end;

  if found then
    Result := drive
  else
    Result := '';

end;

function InitializeSetup(): Boolean;
var firstcd: String;
begin

  firstcd := GetFirstCD();

  // go on with setup only if I found a CD-ROM
  Result := Length(firstcd) > 0;

  if Result then
    MsgBox( Format1( 'First CD-ROM is %s', Uppercase(firstcd) ) , mbInformation, MB_OK )
  else
    MsgBox( 'System has not CD-ROM.' , mbInformation, MB_OK )

end;


31 - How to create a MTS (COM+) package

Article created: 06/11/02 22:37:41
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=31

Author: Rob Segerink, SnapIT

Rob has created a sample ISX script and delphi dll, to show how to can create a MTS/COM+ package.
The dll was created with delphi 5 pro but should also work with delphi 3/4.

Follows sample script how to call DLL, anyway it is possible to downalod a zip file that includes:
- Sample script
- DLL
- DLL source code

;
; ISX 3.0.4-beta
;

[Setup]
AppName=My Com+ dll
AppVerName=My Com+ dll 1.5
AppPublisher=My Company, Inc.
AppPublisherURL=http://www.mycompany.com
AppSupportURL=http://www.mycompany.com
AppUpdatesURL=http://www.mycompany.com
DefaultDirName={pf}\My Com+
DisableProgramGroupPage=True
Uninstallable=false
DisableStartupPrompt=true

[Tasks]
Name: RegisterComPackage; Description: Register Com+ package in Windows Component Services

[Files]
Source: MyCom+.dll; DestDir: {app}; CopyMode: alwaysoverwrite
Source: ComAdm.dll; CopyMode: dontcopy

[Code]

function AddComPackages(PackageName, PackageDir, Packages : PChar): Integer;
external 'AddComPackages@files:ComAdm.dll stdcall';

function InitializeSetup(): Boolean;
begin
  { Let Setup run }
  Result := True;
end;

procedure CurPageChanged(CurPage: Integer);
begin
  case CurPage of
    wpFinished :
      begin
        if ShouldProcessEntry('', 'RegisterComPackage') = srYes then
          {
          If you want to register multiple packages, comma seperate them, for example :
          'MyCom+.dll,COM_ADMINISTRATOR.DLL,COM_MAINTENANCE.DLL'
          }
          if AddComPackages('InnoComPackage', ExpandConstant('{app}'), 'MyCom+.dll') = -1 then
            MsgBox('Error registering Com+ package(s)!', mbError, MB_OK)
          else MsgBox('Succesfull registered Com+ package(s)', mbInformation, MB_OK);

      end;
  end;
end;


32 - Functions to Start, Stop, Install, Remove a NT Service

Article created: 06/11/02 22:48:16
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=32

Author: Silvio Iaccarino

Examples how to fiddle about win32 functions using isx pascal scripts

- Check, install, remove, stop and start NT based services
- Create or update entries in the services file
- Check version of system components

Needs isx 3.0.4 or greater
It is possible anytime download latest functions at Silvio website.

[Setup]
AppName=test
AppVerName=test

CreateAppDir=false
UsePreviousAppDir=false
UsePreviousGroup=false
AlwaysShowComponentsList=false
ShowComponentSizes=false
FlatComponentsList=false
UsePreviousSetupType=false
UsePreviousTasks=false
UsePreviousUserInfo=false
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]
type
    SERVICE_STATUS = record
        dwServiceType				: cardinal;
        dwCurrentState				: cardinal;
        dwControlsAccepted			: cardinal;
        dwWin32ExitCode				: cardinal;
        dwServiceSpecificExitCode	: cardinal;
        dwCheckPoint				: cardinal;
        dwWaitHint					: cardinal;
    end;
    HANDLE = cardinal;

const
    SERVICE_QUERY_CONFIG		= $1;
    SERVICE_CHANGE_CONFIG		= $2;
    SERVICE_QUERY_STATUS		= $4;
    SERVICE_START				= $10;
    SERVICE_STOP				= $20;
    SERVICE_ALL_ACCESS			= $f01ff;
    SC_MANAGER_ALL_ACCESS		= $f003f;
    SERVICE_WIN32_OWN_PROCESS	= $10;
    SERVICE_WIN32_SHARE_PROCESS	= $20;
    SERVICE_WIN32				= $30;
    SERVICE_INTERACTIVE_PROCESS = $100;
    SERVICE_BOOT_START          = $0;
    SERVICE_SYSTEM_START        = $1;
    SERVICE_AUTO_START          = $2;
    SERVICE_DEMAND_START        = $3;
    SERVICE_DISABLED            = $4;
    SERVICE_DELETE              = $10000;
    SERVICE_CONTROL_STOP		= $1;
    SERVICE_CONTROL_PAUSE		= $2;
    SERVICE_CONTROL_CONTINUE	= $3;
    SERVICE_CONTROL_INTERROGATE = $4;
    SERVICE_STOPPED				= $1;
    SERVICE_START_PENDING       = $2;
    SERVICE_STOP_PENDING        = $3;
    SERVICE_RUNNING             = $4;
    SERVICE_CONTINUE_PENDING    = $5;
    SERVICE_PAUSE_PENDING       = $6;
    SERVICE_PAUSED              = $7;

// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenSCManagerA@advapi32.dll stdcall';

function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenServiceA@advapi32.dll stdcall';

function CloseServiceHandle(hSCObject :HANDLE): boolean;
external 'CloseServiceHandle@advapi32.dll stdcall';

function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external 'CreateServiceA@advapi32.dll stdcall';

function DeleteService(hService :HANDLE): boolean;
external 'DeleteService@advapi32.dll stdcall';

function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external 'StartServiceA@advapi32.dll stdcall';

function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'ControlService@advapi32.dll stdcall';

function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';

function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';

function OpenServiceManager() : HANDLE;
begin
    if UsingWinNT() = true then begin
        Result := OpenSCManager('','ServicesActive',SC_MANAGER_ALL_ACCESS);
        if Result = 0 then
            MsgBox('the servicemanager is not available', mbError, MB_OK)
    end
    else begin
            MsgBox('only nt based systems support services', mbError, MB_OK)
            Result := 0;
    end
end;

function IsServiceInstalled(ServiceName: string) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_CONFIG);
        if hService <> 0 then begin
            Result := true;
            CloseServiceHandle(hService)
        end;
        CloseServiceHandle(hSCM)
    end
end;

function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := CreateService(hSCM,ServiceName,DisplayName,SERVICE_ALL_ACCESS,ServiceType,StartType,0,FileName,'',0,'','','');
        if hService <> 0 then begin
            Result := true;
            // Win2K & WinXP supports aditional description text for services
            if Description<> '' then
                RegWriteStringValue(HKLM,'System\CurrentControlSet\Services' + ServiceName,'Description',Description);
            CloseServiceHandle(hService)
        end;
        CloseServiceHandle(hSCM)
    end
end;

function RemoveService(ServiceName: string) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := OpenService(hSCM,ServiceName,SERVICE_DELETE);
        if hService <> 0 then begin
            Result := DeleteService(hService);
            CloseServiceHandle(hService)
        end;
        CloseServiceHandle(hSCM)
    end
end;

function StartService(ServiceName: string) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := OpenService(hSCM,ServiceName,SERVICE_START);
        if hService <> 0 then begin
            Result := StartNTService(hService,0,0);
            CloseServiceHandle(hService)
        end;
        CloseServiceHandle(hSCM)
    end;
end;

function StopService(ServiceName: string) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
    Status	: SERVICE_STATUS;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := OpenService(hSCM,ServiceName,SERVICE_STOP);
        if hService <> 0 then begin
            Result := ControlService(hService,SERVICE_CONTROL_STOP,Status);
            CloseServiceHandle(hService)
        end;
        CloseServiceHandle(hSCM)
    end;
end;

function IsServiceRunning(ServiceName: string) : boolean;
var
    hSCM	: HANDLE;
    hService: HANDLE;
    Status	: SERVICE_STATUS;
begin
    hSCM := OpenServiceManager();
    Result := false;
    if hSCM <> 0 then begin
        hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_STATUS);
        if hService <> 0 then begin
            if QueryServiceStatus(hService,Status) then begin
                Result :=(Status.dwCurrentState = SERVICE_RUNNING)
            end;
            CloseServiceHandle(hService)
            end;
        CloseServiceHandle(hSCM)
    end
end;

// #######################################################################################
// create an entry in the services file
// #######################################################################################
function SetupService(service, port, comment: string) : boolean;
var
    filename	: string;
    s			: string;
    lines		: TArrayOfString;
    n			: longint;
    i			: longint;
    errcode		: integer;
    servnamlen	: integer;
    error		: boolean;
begin
    if UsingWinNT() = true then
        filename := ExpandConstant('{sys}\drivers\etc\services')
    else
        filename := ExpandConstant('{win}\services');

    if LoadStringsFromFile(filename,lines) = true then begin
        Result		:= true;
        n			:= GetArrayLength(lines) - 1;
        servnamlen	:= Length(service);
        error		:= false;

        for i:=0 to n do begin
            if Copy(lines[i],1,1) <> '#' then begin
                s := Copy(lines[i],1,servnamlen);
                if CompareText(s,service) = 0 then
                    exit; // found service-entry

                if Pos(port,lines[i]) > 0 then begin
                    error := true;
                    lines[i] := '#' + lines[i] + '   # disabled because collision with  ' + service + ' service';
                end;
            end
            else if CompareText(Copy(lines[i],2,servnamlen),service) = 0 then begin
                // service-entry was disabled
                Delete(lines[i],1,1);
                Result := SaveStringsToFile(filename,lines,false);
                exit;
            end;
        end;

        if error = true then begin
            // save disabled entries
            if SaveStringsToFile(filename,lines,false) = false then begin
                Result := false;
                exit;
            end;
        end;

        // create new service entry
        s := service + '       ' + port + '           # ' + comment + #13#10;
        if SaveStringToFile(filename,s,true) = false then begin
            Result := false;
            exit;
        end;

        if error = true then begin
            MsgBox('the ' + service + ' port was already used. The old service is disabled now. You should check the services file manually now.',mbInformation,MB_OK);
            InstExec('notepad.exe',filename,GetCurrentDir(),true,false,SW_SHOWNORMAL,errcode);
        end;
    end
    else
        Result := false;
end;

// #######################################################################################
// version functions
// #######################################################################################
function CheckVersion(Filename : string;hh,hl,lh,ll : integer) : boolean;
var
    VersionMS	: cardinal;
    VersionLS	: cardinal;
    CheckMS		: cardinal;
    CheckLS		: cardinal;
begin
    if GetVersionNumbers(Filename,VersionMS,VersionLS) = false then
        Result := false
    else begin
        CheckMS := (hh shl $10) or hl;
        CheckLS := (lh shl $10) or ll;
        Result := (VersionMS > CheckMS) or ((VersionMS = CheckMS) and (VersionLS >= CheckLS));
    end;
end;

// Some examples for version checking
function NeedShellFolderUpdate() : boolean;
begin
    Result := CheckVersion('ShFolder.dll',5,50,4027,300) = false;
end;

function NeedVCRedistUpdate() : boolean;
begin
    Result := (CheckVersion('mfc42.dll',6,0,8665,0) = false)
        or (CheckVersion('msvcrt.dll',6,0,8797,0) = false)
        or (CheckVersion('comctl32.dll',5,80,2614,3600) = false);
end;

function NeedHTMLHelpUpdate() : boolean;
begin
    Result := CheckVersion('hh.exe',4,72,0,0) = false;
end;

function NeedWinsockUpdate() : boolean;
begin
    Result := (UsingWinNT() = false) and (CheckVersion('mswsock.dll',4,10,0,1656) = false);
end;

function NeedDCOMUpdate() : boolean;
begin
    Result := (UsingWinNT() = false) and (CheckVersion('oleaut32.dll',2,30,0,0) = false);
end;

// function IsServiceInstalled(ServiceName: string) : boolean;
// function IsServiceRunning(ServiceName: string) : boolean;
// function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
// function RemoveService(ServiceName: string) : boolean;
// function StartService(ServiceName: string) : boolean;
// function StopService(ServiceName: string) : boolean;

// function SetupService(service, port, comment: string) : boolean;

// function CheckVersion(Filename : string;hh,hl,lh,ll : integer) : boolean;

function InitializeSetup(): boolean;
begin
    if IsServiceInstalled('myservice') = false then begin
        if InstallService('c:\winnt\system32\myservice.exe','myservice','my service','my service is doing usefull things',SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START) = true then begin
            StartService('myservice');
            StopService('myservice');
            // after stopping a service you should wait some seconds before removing
            RemoveService('myservice');
            // otherwise removing can fail
        end
    end
    else if IsServiceRunning('myservice') then
        MsgBox('myservice is running',mbInformation, MB_OK);

    Result := false
end;


33 - How to create a ODBC DSN (2)

Article created: 17/12/02 23:07:00
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=33

Authors: Vincenzo Giordano, Lambert Antonio

Lambert Antonio creates a DSN using function SQLConfigDataSource of ODBCCP32.DLL, starting from this idea, sample code of this article shows how to create a DSN ODBC specifyng all attributes, it is possible specify where to create it, User or System DSN.

This sample could be also used to "manage" DSN modifying or deleting them. With a task it is possible to choose if a driver dialog configuration have to be shown.

;
; ISX 3.0.4

[Setup]
AppName=ODBC2
AppVerName=ODBC2
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\ODBC2
DisableStartupPrompt=true
CreateAppDir=false

[_ISTool]
EnableISX=true

[Tasks]
Name: cfgdsndlg; Description: Config DSN with dialog; GroupDescription: Configuration:; Flags: unchecked

[Code]

function SQLConfigDataSource(hwndParent: LongInt; fRequest: LongInt;
lpszDriver: String; lpszAttributes: String): LongInt;
external 'SQLConfigDataSource@ODBCCP32.DLL stdcall';

const
  ODBC_ADD_DSN = 1;         // Add data source
  ODBC_CONFIG_DSN = 2;      // Configure (edit) data source
  ODBC_REMOVE_DSN = 3;      // Remove data source
  ODBC_ADD_SYS_DSN = 4;	    // add a system DSN
  ODBC_CONFIG_SYS_DSN = 5;  // Configure a system DSN
  ODBC_REMOVE_SYS_DSN = 6;  // remove a system DSN

function CreateDSN( showdlg: Boolean) : LongInt;
var
  hwnd : LongInt;
var
  strDriver, strAttributes, NewLine : String;
begin
    // if it is not auto configuration, setting HWND of wizard let show
    // DSN setup dialog with attributes specified
    if not showdlg then
      hwnd := 0
    else
      hwnd := StrToInt(ExpandConstant('{wizardhwnd}'));

    // MYSQL Sample
    {
    strDriver := 'MySQL';
    NewLine := Chr(0);
    strAttributes := 'SERVER=localhost' + NewLine +
    'DESCRIPTION=MySQL Driver DSN' + NewLine +
    'DSN=SAMPLE_DSN' + NewLine +
    'DATABASE=test' + NewLine +
    'UID=username' + NewLine +
    'PASSWORD=password' + NewLine +
    'PORT=3306' + NewLine +
    'OPTION=3' + NewLine;
    }

    // SQL Server Sample
    strDriver := 'SQL Server';
    NewLine := Chr(0);
    strAttributes := 'SERVER=(local)' + NewLine +
    'Description=SQL Server Driver DSN' + NewLine +
    'DSN=SQLSERVER_SAMPLE' + NewLine +
    'Trusted_Connection=Yes' + NewLine;

    Result := SQLConfigDataSource( hwnd, ODBC_ADD_SYS_DSN, strDriver, strAttributes);
end;

function NextButtonClick(CurPage: Integer): Boolean;
var showdlg : Boolean;
begin

  // by default go to next page
  Result := true;

  // if curpage is wpSelectTasks check config DSN
  if CurPage = wpSelectTasks then
  begin

    showdlg := ShouldProcessEntry( '','cfgdsndlg') = srYes ;

    if CreateDSN( showdlg  ) = 0 then
      MsgBox( 'DSN not done.', mbError, MB_OK )
    else
      MsgBox( 'DSN Created !', mbInformation, MB_OK );

  end;

end;


34 - How to detect Acrobat Reader version installed

Article created: 20/12/02 22:30:01
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=34

This script is useful to detect if, and which version of Adobe Acrobat reader is installed on the PC.

;
; ISX 3.0.4
;
;
[Setup]
AppName=Acrobat
AppVerName=Acrobat
DefaultDirName={pf}\Acrobat
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=Acrobat
CreateAppDir=false

[Code]

function GetAcrobatReaderVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe', '', sVersion );
  GetVersionNumbersString( sVersion , sVersion );
  Result := sVersion;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin

  // by default go to next page
  Result := true;

  if CurPage = wpWelcome then
  begin

    if Length( GetAcrobatReaderVersion() ) = 0 then
    begin
      MsgBox( 'There is not installed Acrobat reader',  mbInformation, MB_OK );
      Result := false;
    end
    else
      MsgBox( 'Acrobat reader installed is version ' + GetAcrobatReaderVersion() ,  mbInformation, MB_OK );

  end;

end;


35 - How to add a printer

Article created: 20/12/02 21:30:01
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=35

This script uses API function AddPrinter to add a printer in the system, this function is exported by winspool.drv and uses structure PRINTER_INFO_2.

This function fails if printer driver is not installed, to do that can be used function AddPrinterDriver.

;
; ISX 3.0.4
;
;
[Setup]
AppName=AddPrinter
AppVerName=AddPrinter
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\AddPrinter
DisableStartupPrompt=true
CreateAppDir=false

[_ISTool]
EnableISX=true

[Code]

{ C declaration
 typedef struct _PRINTER_INFO_2 {
  LPTSTR    pServerName;
  LPTSTR    pPrinterName;
  LPTSTR    pShareName;
  LPTSTR    pPortName;
  LPTSTR    pDriverName;
  LPTSTR    pComment;
  LPTSTR    pLocation;
  LPDEVMODE pDevMode;
  LPTSTR    pSepFile;
  LPTSTR    pPrintProcessor;
  LPTSTR    pDatatype;
  LPTSTR    pParameters;
  PSECURITY_DESCRIPTOR pSecurityDescriptor;
  DWORD     Attributes;
  DWORD     Priority;
  DWORD     DefaultPriority;
  DWORD     StartTime;
  DWORD     UntilTime;
  DWORD     Status;
  DWORD     cJobs;
  DWORD     AveragePPM;
} // PRINTER_INFO_2, *PPRINTER_INFO_2;

//BOOL ClosePrinter(
//  HANDLE hPrinter // handle to printer object
//  );

// HANDLE AddPrinter(
//  LPTSTR *pName,     // server name
//DWORD Level,      // printer information level
//LPBYTE pPrinter // printer information buffer
//);

type
  TPrinterInfo2 = record
   pServerName : PChar;
   pPrinterName : PChar;
   pShareName : PChar;
   pPortName : PChar;
   pDriverName : PChar;
   pComment : PChar;
   pLocation : PChar;
   pDevMode : PChar; // LPDEVMODE
   pSepFile : PChar;
   pPrintProcessor : PChar;
   pDatatype : PChar;
   pParameters : PChar;
   pSecurityDescriptor : PChar; // PSECURITY_DESCRIPTOR
   Attributes : LongInt;
   Priority : LongInt;
   DefaultPriority : LongInt;
   StartTime : LongInt;
   UntilTime : LongInt;
   Status : LongInt;
   cJobs : LongInt;
   AveragePPM : LongInt;
  end;

  Handle = Cardinal;

function ClosePrinter( pPrinter: Handle ): Boolean; external
'ClosePrinter@winspool.drv stdcall';

function AddPrinter( pName : PChar; Level: Longint; var pPrinter2: TPrinterInfo2): Handle; external
'AddPrinterA@winspool.drv stdcall';

function GetLastError() : Integer; external 'GetLastError@kernel32.dll';

function InitializeSetup: Boolean;
var
  P2: TPrinterInfo2;
  hp: Handle;
  ercode: Integer;
begin

  Result := true;

  P2.pPrinterName := 'TestPrinter';
  P2.pDriverName := 'WinFax';
  P2.pPrintProcessor := 'WinPrint';
  P2.pPortName := 'FILE:';
  P2.pComment := 'Test printer installation';

  hp := AddPrinter( CastIntegerToString(0), 2, P2 );

  if ( hp <> 0 ) then
    ClosePrinter( hp )
  else
  begin
    Result := false;
    ercode := GetLastError();
    // error codes samples
    // 1801 Printer Name not valid
    // 1802 Printer already exists
    // 1706 Port specified unknown
    // 1798 PrintProcessor unknown
    // 1797 PrinterDriver unkown
    MsgBox( 'Error ' + IntToStr(ercode) + ': ' + SysErrorMessage( ercode ), mbError, MB_OK );
  end;

end;


36 - How to install a screensaver

Article created: 20/12/02 21:36:30
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=36

Author: Schwarz Heiko

This script shows how to install a screensaver.

Details on installing a screensaver can be found at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/regentry/34635.asp

;
; Details on installing a screensaver can be found at:
; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/regentry/34635.asp
;
; ISX 2.0.18
;

[Files]
; Install new screensaver into system/system32
Source: "Screensaver.scr"; DestDir: "{sys}"; CopyMode: alwaysoverwrite

[Run]
; Open screensaver property dialog with new screensaver selected
Filename: "{sys}\rundll32.exe"; Parameters: "desk.cpl,InstallScreenSaver {sys}\Screensaver.scr"


37 - How to browse for a file

Article created: 16/01/03 23:00:33
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=37

This sample show how to bwose for a file in setup functions. It's based on this aticle posted by Martijn Laan, it shows how to use OPENFILENAME structure with GetOpenFileName Win32 API.

From Inno Setup version 4.0.3 there is a function GetOpenFileName that call the browse for a file dialog.

;
; ISX 3.0.6.1
;

[Setup]
AppName=BrowseFile
AppVerName=BrowseFile
DefaultDirName=BrowseFile
DisableStartupPrompt=true
Uninstallable=false
Compression=bzip
CreateAppDir=false
DisableProgramGroupPage=true

[Tasks]
Name: askini; Description: Choose a custom INI file; GroupDescription: Configuration:

[Code]

type

  TOpenFileName = record
    lStructSize: Longint;
    hwndOwner: Longint;
    hInstance: Longint;
    lpstrFilter: PChar;
    lpstrCustomFilter: PChar;
    nMaxCustFilter: Longint;
    nFilterIndex: Longint;
    lpstrFile: PChar;
    nMaxFile: Longint;
    lpstrFileTitle: PChar;
    nMaxFileTitle: Longint;
    lpstrInitialDir: PChar;
    lpstrTitle: PChar;
    Flags: Longint;
    nFileOffset: Word;
    nFileExtension: Word;
    lpstrDefExt: PChar;
    lCustData: Longint;
    lpfnHook: Longint;
    lpTemplateName: PChar;
  end;

function GetOpenFileName(var lpofn: TOpenFileName): Boolean; external
'GetOpenFileNameA@comdlg32.dll';

Const OFN_HIDEREADONLY = $4;

function NextButtonClick(CurPage: Integer): Boolean;
var
  F: TOpenFileName;
  S: String; z : char;
  hwnd: Longint;
begin

  // by default go to next page
  Result := true;

  z := Chr(0);
  hwnd := StrToInt(ExpandConstant('{wizardhwnd}'));

  // if curpage is wpSelectTasks
  if CurPage = wpSelectTasks then
  begin

    if ShouldProcessEntry( '','askini') = srYes then
    begin

      // set defailt file
      S := 'config.ini';
      // get enough space
      SetLength(S, 512);
      // set wizard HWND as owner
      F.hwndOwner := hwnd;
      // set structure size
      F.lStructSize := 76;
      // set S buffer for filename
      F.lpstrFile := S;
      F.nMaxFile := Length(S);
      // set dialog title
      F.lpstrTitle := 'Choose configuration file';
      // set filters
      F.lpstrFilter := 'INI files (*.ini)' + z + '*.ini' + z +
                       'All files (*.*)' + z + '*.*' + z + z ;
      // set flags
      F.Flags := OFN_HIDEREADONLY;
      // set initial dir as system
      F.lpstrInitialDir := ExpandConstant('{sys}');

      if GetOpenFileName(F) then begin
           MsgBox( 'File choosed:'+ #13 + #13 + S, 0, 0);
           Result := true;
      end
      else
          Result := false;

    end;

  end;

end;


38 - How to detect Excel is running by window class name

Article created: 16/01/03 23:20:34
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=38

This sample shows how to detect if MS Excel is running looking for it's window class name XLMAIN.

It is possible to look for window class name with Spy++, a Visual Studio utility, or with free Winspector.

;
; ISX 3.0.6.1
;
;
; Free tool for read windows class name
; http://www.gipsysoft.com/articles/winspector/
;

[Setup]
AppName=ExcelRunning
AppVerName=ExcelRunning
DefaultDirName={pf}\ExcelRunning
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=ExcelRunning
CreateAppDir=false
DisableProgramGroupPage=true

[Code]

function IsExcelRunning(): Boolean;
var hWnd: Longint;
begin
 Result := false;
 hWnd := FindWindowByClassName('XLMAIN');
 if hWnd>0 then Result := true;
end;

function InitializeSetup(): Boolean;
begin

  if IsExcelRunning() then
  begin

      MsgBox( 'MS Excel is running' + #13 +
       'Please shutdown it and start again this setup.',
        mbError, MB_OK );

      // cannot go with setup anyway
      Result := false;

  end
  else begin

    // go on with setup
    Result := true;
  end;

end;


39 - Two utilities for environment variables, hosts and services changing

Article created: 18/01/03 0:38:22
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=39

Author: Andrey Adamovich

We wrote two utilities to simplify some installation tasks:
1) TCPInst.exe

to change "services" un "hosts" on any system (Win9x and WinNT)

   TCP install tool verion 1.0.2
   Copyright (C) 2001-2003 - SolCraft Ltd.
   This may be freely redistributed
   Usage:
       -help                  - show this message
       -addservice            - add tcp/ip service to SERVICES file
           -name=      - service name
           -port=      - port number
           -protocol=  - protocol
           -aliases=   - aliases
           -comment=   - comment line for service
       -addhost               - add tcp/ip host to HOSTS file
           -name=      - host name
           -ipaddress= - IP address for host
           -comment=   - comment line for host

2) EnvInst.exe

to change environment variables on NT systems
(can change PATH-like variables)

   Environment install tool verion 1.0.0.11
   Copyright (C) 2003 - SolCraft Ltd.
   This may be freely redistributed
   Usage:
    COMMANDS:
       -help                  - show this message
       -helplist              - show additional list variable options
       -broadcast          - broadcast message about environment changes
       -setvar                - set/create environment variable
           -name=      - variable name
           -value=     - variable value
       -unsetvar              - delete environment variable
           -name=      - variable name
       -addval                - add value to environment variable
           -name=      - variable name
           -value=     - new list value
       -eraseval              - erase value from environment variable
           -name=      - variable name
           -value=     - new list value
    SCOPE OPTIONS:
       -user                  - change user environment
       -system                - change system environment
    LIST OPTIONS:
       -delimiter=     - list delimeter (default ';')
       -append                - append value to the end of list
       -prepend               - prepend value in the begining
       -cleardups             - clear duplicates on write
       -clearempty            - clear empty strings
       -sort                  - sort values on write
       -forceadd              - add value even if its duplicate found

BUG fixes:
* append and prepend options work correctly
* list values with spaces are not quoted after processing
* should correctly work on -eraseval
* don't resort values on -cleardups

Features:

* -clearepmty flag to clear empty values

Author made some bug fixes and added some features
to envinst.exe and tcpinst.exe:

* -removeservice and -removehost options in tcpinst.exe
* -silent option now is seen in help message
* new icons for both programs
* minor fixes

Examples for tcpinst:

Command line:

tcpinst.exe -addhost -ipaddress=192.168.1.222 -name=server -comment="# linux server"

tcpinst.exe -addservice -name=gds_db -port=3050 -protocol=tcp -comment="# InterBase Server"
It is possible to download them here.

;
;usage sample:
;

[RUN]
Components: SERVER; Filename: {app}\uninstall\envinst.exe; Parameters: "-silent -broadcast -addval -name=path -value=""{app}\lib\server"" -append"

;
;P.S. -silent is recommended for use in install scripts
;

[RUN]
Components: SERVER; Filename: {app}\uninstall\tcpinst.exe;
Parameters: "-addhost -ipaddress=192.168.1.222 -name=server -comment=""# linux server"""

Components: SERVER; Filename: {app}\uninstall\tcpinst.exe;
Parameters: "-addservice -name=gds_db -port=3050 -protocol=tcp -comment=""# InterBase Server"""


40 - Better way to install MSDE

Article created: 25/01/03 11:00:04
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=40

Author: Peter Wagner

I readed your sample of how to install MSDE with Inno...
Well, I havent installed MSDE, but I know some thinks for installing SQL Server.

Here it goes:
One method of handling a new installation for a client system is to use a batch
file and a initialization file. The initialization file will contain all the instructions
needed by SQL Servers setup program so that the install can be done unattended.
If there's a need to create one batch, you can launch the setup program with an
option that will save your choices into an initialization file.
You also can cancel the install at the last step and the file will still be created.
The last step will be to include two extra sections needed for the install process.
These are the [SdStartCopy-0] and [SdFinish-0] sections.

The following command creates the initialization file:

setupsql.exe k=Rc

This will create a file called setup.iss in the \Windows or \WinNT directory.
To use the initialization file that is created, use the following code:

start /wait setupsql.exe f1 C:\WinNT\setup.iss SMS s

The "start /wait" together with the SMS switch forces the installation to
complete without returning to the command prompt until the setup is finished.

The f1 switch must specify the full path and name of the initialization file that you create.

The s switch causes it to run in silent mode and never present any screen to the user.

You must include a full path to the setupsql program.

If you are controlling the installation through a batch, then youll also need to allow the
user to choose the location of the SQL Server software and databases.
This choice will be for both the computer where SQL Server will reside as well as the
folders where the program and databases will be placed.

So with this information, I thing if you generate a batch file, add this to inno to install it in some
Directory, you could programatically make the MSDE install using this batch file.

Hope it helps in finding a better way to install MSDE.

Peter


41 - How to check for JDK

Article created: 25/01/03 11:06:25
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=41

Author: hostmonsta

For everyone who's interested in checking for JDK (Java Development Kit), here's the code:

[Code]
//* Getting Java version from registry *//
function getJavaVersion(): String;
var
     javaVersion: String;
begin
     javaVersion := '';
     RegQueryStringValue(HKLM, 'SOFTWARE\JavaSoft\Java Runtime Environment', 'CurrentVersion', javaVersion);
     GetVersionNumbersString(javaVersion, javaVersion);
     Result := javaVersion;
end;

//* Called on setup startup //*
function InitializeSetup(): Boolean;
begin
     Result := true;
     if Length( getJavaVersion() ) = 0 then begin
          //* No Java detected *//
          MsgBox('Setup detected, that there is no Java Runtime installed.' + #13 +
                         'You must have installed at least Java Runtime 1.2.2 or higher to continue setup.' + #13 +
                         'Please install one from http://java.sun.com and restart setup.', mbInformation, MB_OK);
          Result := false;
          end
     else begin
          //* Java version lower than 1.2.2 detected *//
          if (getJavaVersion()) < '1.2.2' then begin
               MsgBox('Setup detected, that you have not installed at least Java Runtime 1.2.2.' + #13 +
                         'Please install and run setup again!', mbInformation, MB_OK);
               Result := false
          end
     end;
end;


42 - How to ask for a drive to install

Article created: 25/01/03 12:27:32
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=42

This script shows how to prompt for a drive letter where to install, instead of ask for a drive and a folder.

See also How to ask for a drive to install for IS 5

;
; ISX 3.0.6.1
;

[Setup]
AppName=SelDrive
AppVerName=SelDrive
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={code:GetAppDir|{pf}\SelDrive}
DisableStartupPrompt=true
CreateAppDir=false

[_ISTool]
EnableISX=true

[Files]
;Main program that will be installed  folder
Source: MyProgram.exe; DestDir: {code:GetAppDir|''}

[Code]
var
  AppDir: String;
  DrivePrompts, DriveValues, DrvLetters: array of String;

function GetDriveType( lpDisk: String ): Integer;
external 'GetDriveTypeA@kernel32.dll stdcall';

function GetLogicalDriveStrings( nLenDrives: LongInt; lpDrives: String ): Integer;
external 'GetLogicalDriveStringsA@kernel32.dll stdcall';

const
  DRIVE_UNKNOWN = 0; // The drive type cannot be determined.
  DRIVE_NO_ROOT_DIR = 1; // The root path is invalid. For example, no volume is mounted at the path.
  DRIVE_REMOVABLE = 2; // The disk can be removed from the drive.
  DRIVE_FIXED = 3; // The disk cannot be removed from the drive.
  DRIVE_REMOTE = 4; // The drive is a remote (network) drive.
  DRIVE_CDROM = 5; // The drive is a CD-ROM drive.
  DRIVE_RAMDISK = 6; // The drive is a RAM disk.

// function to convert disk type to string
function DriveTypeString( dtype: Integer ): String ;
begin
  case dtype of
    DRIVE_NO_ROOT_DIR : Result := 'Root path invalid';
    DRIVE_REMOVABLE : Result := 'Removable';
    DRIVE_FIXED : Result := 'Fixed';
    DRIVE_REMOTE : Result := 'Network';
    DRIVE_CDROM : Result := 'CD-ROM';
    DRIVE_RAMDISK : Result := 'Ram disk';
  else
    Result := 'Unknown';
  end;
end;

function InitializeSetup(): Boolean;
var
  n: Integer;
  drivesletters: String; lenletters: Integer;
  drive: String;
  disktype, posnull: Integer;

begin

  //get all drives letters of system
  drivesletters := StringOfChar( ' ', 64 );
  lenletters := GetLogicalDriveStrings( 63, drivesletters );
  SetLength( drivesletters , lenletters );

  drive := '';
  n := 0;
  while ( (Length(drivesletters) > 0) ) do
  begin
    posnull := Pos( #0, drivesletters );
      if posnull > 0 then
      begin
      drive:= UpperCase( Copy( drivesletters, 1, posnull - 1 ) );

      // get number type of disk
      disktype := GetDriveType( drive );

      // add it only if it is not a floppy
      if ( not ( disktype = DRIVE_REMOVABLE ) ) then
      begin

        SetArrayLength(DrivePrompts, N+1);
        SetArrayLength(DriveValues, N+1);
        SetArrayLength(DrvLetters, N+1);

        DrivePrompts[n] := drive + '  ' + DriveTypeString( disktype );
        DrvLetters[n] := drive;
        // default select C: Drive
        if ( drive = 'C:' ) then DriveValues[n] := '1';
        n := n + 1;
      end

        drivesletters := Copy( drivesletters, posnull+1, Length(drivesletters));
      end
  end;

  Result := True;
end;

function GetDriveChoosed : String;
var i:Integer;
begin
  for i:=0 to GetArrayLength(DriveValues)-1 do
    begin
      if DriveValues[i]='1' then
        begin
          Result:=DrvLetters[i];
          Exit;
        end;
    end;
  Result:='?';
end;

function PromptDrive( BackClicked: Boolean): Boolean;
var
  Next: Boolean;
begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('Select Installation drive');
    ScriptDlgPageSetSubCaption1( 'Where should program be installed' );
    ScriptDlgPageSetSubCaption2('Select the drive where you would like program to be installed, then click Next..');

    // Ask for a dir until the user has entered one or click Back or Cancel
    Next:= InputOptionArray(DrivePrompts, DriveValues, True, True);

    // store drive choosed
    if Next then
      AppDir := GetDriveChoosed;

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    // Insert a custom wizard page between two non custom pages
    Result := PromptDrive( BackClicked );
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;

function GetAppDir(S: String): String;
begin
  // Return the selected AppDir
  Result := AppDir;
end;

// skip Select dir wizard page
function SkipCurPage(CurPage: Integer): Boolean;
begin
  if ( CurPage = wpSelectDir ) then
    Result := true
  else
    Result := false;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  // Fill the 'Ready Memo' with the normal settings and the custom settings
  S := 'Program Location'+ NewLine;

  S := S + Space + GetAppDir('') + NewLine + NewLine;

  Result := S;
end;


43 - How to prevent user to choose an install folder with spaces

Article created: 02/02/03 16:10:32
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=43

This script show how to prevent users to choose an install folder with spaces. {pf} may resolve in different ways, C:\Programmi (Italian), C:\Program Files (English), C:\Programme (german). Is some cases it is mandatory to install in a folder with no spaces.
This script base install folder on {pf} constant, if this folder has spaces suggest a modified folder, anyway is user choose a folder wirh spaces, installer warn user.
Bo Berglund provide code to remove spaces instead of cut folder to a first space.

;
;
; ISX 3.0.6.1

[Setup]
AppName=NoFolderSpace
AppVerName=NoFolderSpace
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={code:GetDefDir|{pf}\NoFolderSpace}
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

function NextButtonClick(CurPage: Integer): Boolean;
var
    folder: String;
begin

  // by default go to next page
  Result := true;

  // if curpage is wpSelectDir check is filesystem
  if CurPage = wpSelectDir then
  begin

    folder := WizardDirValue;

    // debug purpose, show folder selected
      // MsgBox( folder, mbInformation, MB_OK );

    if Pos( ' ', folder ) > 0 then
    begin
      MsgBox( 'Folder selectd contains spaces, please select a folder without spaces in it.', mbInformation, MB_OK );

      Result := false;
    end

  end;

end;

function GetDefDir( def: String ) : String;
begin

  // if default folder contains spaces, suggest user to install in folderwithnospaces\Myappdir
  if Pos( ' ', def ) > 0 then
  begin
    def := Copy( def, 1, Pos( ' ', def ) - 1 ) + '\NoFolderSpace';
  end;

  // Bo Berglund code
  // if default folder contains spaces, remove them
  {
  while Pos( ' ', def ) > 0 do
  begin
      p := Pos(' ', def);
      def := Copy(def, 1, p-1) + Copy(def, p+1, Length(def));
  end;
  }

  // debug purpose, show folder selected
  // MsgBox( def, mbInformation, MB_OK );

  Result := def;
end;


44 - Setting ScreenSaver on/off and updating timeout after install

Article created: 19/03/03 22:55:49
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=44

Author: Zsolt

Setting ScreenSaver on/off and updating timeout after install

[Code]

const
  SPI_SETSCREENSAVETIMEOUT = 15;
  SPI_SETSCREENSAVEACTIVE = 17;

function SystemParametersInfo(uAction, uParam : integer; pvParam : Longint;
 fUpdateProfile : integer): Boolean;
 external 'SystemParametersInfoA@user32.dll';

procedure CurStepChanged(CurStep: Integer);
begin
   if CurStep = csFinished then
   begin
       // 2nd parameter is the time out in seconds
       SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 60, 0, 2);

       // 2nd parameter is 1 if the screensaver should be active,
       // 0 if the screensaver is to be switched off
       SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, 0, 2);

   end;
end;


45 - Native ISX procedures for PATH modification

Article created: 02/05/03 17:00:12
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=45

Author: Thomas Vedel

Native ISX procedures for PATH modification.

Sample usage:

#include "ModifyPath.iss"

[Setup]
OutputDir=ModifyPath
Compression=bzip
AppName=ModifyPath
CreateAppDir=false
DisableStartupPrompt=true
AppVerName=ModifyPath 1.0 beta 1

[_ISTool]
EnableISX=true

[Code]
function InitializeSetup(): boolean;
begin
  ModifyPath('c:\MyTestDir', pmRemoveSubdirsAlso, psAllUsers);
  Result := False;
end;
ModifyPath.iss:

[Code]

// Version log:
// 03/31/2003: Initial release (thv(at)lr.dk)

const
  // Modification method
  pmAddToBeginning = $1;      // Add dir to beginning of Path
  pmAddToEnd = $2;            // Add dir to end of Path
  pmAddAllways = $4;          // Add also if specified dir is already included in existing path
  pmAddOnlyIfDirExists = $8;  // Add only if specified dir actually exists
  pmRemove = $10;             // Remove dir from path

  pmRemoveSubdirsAlso = $20;  // Remove dir and all subdirs from path

  // Scope
  psCurrentUser = 1;          // Modify path for current user
  psAllUsers = 2;             // Modify path for all users

  // Error results
  mpOK = 0;                   // No errors
  mpMissingRights = -1;       // User has insufficient rights
  mpAutoexecNoWriteacc = -2;  // Autoexec can not be written (may be readonly)
  mpBothAddAndRemove = -3;    // User has specified that dir should both be removed from and added to path

{ Helper procedure: Split a path environment variable into individual dirnames }
procedure SplitPath(Path: string; var Dirs: TStringList);
var
  pos: integer;
  s: string;
begin
  Dirs.Clear;
  s := '';
  pos := 1;
  while (pos<=Length(Path)) do
  begin
    if (Path[pos]<>';') then
      s := s + Path[pos];
    if ((Path[pos]=';') or (pos=Length(Path))) then
    begin
      s := Trim(s);
      s := RemoveQuotes(s);
      s := Trim(s);
      if (s <> '') then
        Dirs.Add(s);
      s := '';
    end;
    Pos := Pos + 1;
  end;
end; // procedure SplitPath

{ Helper procedure: Concatenate individual dirnames into a path environment variable }
procedure ConcatPath(Dirs: TStringList; Quotes: boolean; var Path: string);
var
  Index, MaxIndex: integer;
  s: string;
begin
  MaxIndex := Dirs.Count-1;
  Path := '';
  for Index := 0 to MaxIndex do
  begin
    s := Dirs.Strings[Index];
    if ((Quotes) and (pos(' ',s) > 0)) then
      s := AddQuotes(s);
    Path := Path + s;
    if (Index < MaxIndex) then
      Path := Path + ';'
  end;
end; // procedure ConcatPath

{ Helper function: Modifies path environment string }
procedure ModifyPathString(OldPath, DirName: string; Method: integer; Quotes: boolean; var ResultPath: string);
var
  Dirs: TStringList;
  DirNotInPath: Boolean;
  i: integer;
begin
  // Create Dirs variable
  Dirs := TStringList.Create;

  // Remove quotes form DirName
  DirName := Trim(DirName);
  DirName := RemoveQuotes(DirName);
  DirName := Trim(DirName);

  // Split old path in individual directory names
  SplitPath(OldPath, Dirs);

  // Check if dir is allready in path
  DirNotInPath := True;
  for i:=Dirs.Count-1 downto 0 do
  begin
    if (uppercase(Dirs.Strings[i]) = uppercase(DirName)) then
      DirNotInPath := False;
  end;

  // Should dir be removed from existing Path?
  if ((Method and (pmRemove or pmRemoveSubdirsAlso)) > 0) then
  begin
    for i:=Dirs.Count-1 downto 0 do
    begin
      if (((Method and pmRemoveSubdirsAlso) > 0) and (pos(uppercase(DirName)+'', uppercase(Dirs.Strings[i])) = 1)) or
         (((Method and (pmRemove) or (pmRemoveSubdirsAlso)) > 0) and (uppercase(DirName) = uppercase(Dirs.Strings[i])))
      then
        Dirs.Delete(i);
    end;
  end;

  // Should dir be added to existing Path?
  if ((Method and (pmAddToBeginning or pmAddToEnd)) > 0) then
  begin
    // Add dir to path
    if (((Method and pmAddAllways) > 0) or DirNotInPath) then
    begin
      // Dir is not in path allready or should be added anyway
      if (((Method and pmAddOnlyIfDirExists) = 0) or (DirExists(DirName))) then
      begin
        // Dir actually exsists or should be added anyway
        if ((Method and pmAddToBeginning) > 0) then
          Dirs.Insert(0, DirName)
        else
          Dirs.Append(DirName);
      end;
    end;
  end;

  // Concatenate directory names into one single path variable
  ConcatPath(Dirs, Quotes, ResultPath);
  // Finally free Dirs object
  Dirs.Free;
end; // ModifyPathString

{ Helper function: Modify path on Windows 9x }
function ModifyPath9x(DirName: string; Method: integer): integer;
var
  AutoexecLines: TStringList;
  ActualLine: String;
  PathLineNos: TStringList;
  FirstPathLineNo: Integer;
  OldPath, ResultPath: String;
  LineNo, CharNo, Index: integer;

  TempString: String;
begin
  // Expect everything to be OK
  result := mpOK;

  // Create stringslists
  AutoexecLines := TStringList.Create;
  PathLineNos := TStringList.Create;

  // Read existing path
  OldPath := '';
  LoadStringFromFile('c:\Autoexec.bat', TempString);
  AutoexecLines.Text := TempString;
  PathLineNos.Clear;
  // Read Autoexec line by line
  for LineNo := 0 to AutoexecLines.Count - 1 do begin
    ActualLine := AutoexecLines.Strings[LineNo];
    // Check if line starts with "PATH=" after first stripping spaces and other "fill-chars"
    if Pos('=', ActualLine) > 0 then
    begin
      for CharNo := Pos('=', ActualLine)-1 downto 1 do
        if (ActualLine[CharNo]=' ') or (ActualLine[CharNo]=#9) then
          Delete(ActualLine, CharNo, 1);
      if Pos('@', ActualLine) = 1 then
        Delete(ActualLine, 1, 1);
      if (Pos('PATH=', uppercase(ActualLine))=1) or (Pos('SETPATH=', uppercase(ActualLine))=1) then
      begin
        // Remove 'PATH=' and add path to "OldPath" variable
        Delete(ActualLine, 1, pos('=', ActualLine));
        // Check if an earlier PATH variable is referenced, but there has been no previous PATH defined in Autoexec
        if (pos('%PATH%',uppercase(ActualLine))>0) and (PathLineNos.Count=0) then
          OldPath := ExpandConstant('{win}') + ';' + ExpandConstant('{win}')+'\COMMAND';
        if (pos('%PATH%',uppercase(ActualLine))>0) then
        begin
          ActualLine := Copy(ActualLine, 1, pos('%PATH%',uppercase(ActualLine))-1) +
                        OldPath +
                        Copy(ActualLine, pos('%PATH%',uppercase(ActualLine))+6, Length(ActualLine));
        end;
        OldPath := ActualLine;

        // Update list of line numbers holding path variables
        PathLineNos.Add(IntToStr(LineNo));
      end;
    end;
  end;

  // Save first line number in Autoexec.bat which modifies path environment variable
  if PathLineNos.Count > 0 then
    FirstPathLineNo := StrToInt(PathLineNos.Strings[0])
  else
    FirstPathLineNo := 0;

  // Modify path
  ModifyPathString(OldPath, DirName, Method, True, ResultPath);

  // Write Modified path back to Autoexec.bat
  // First delete all existing path references from Autoexec.bat
  Index := PathLineNos.Count-1;
  while (Index>=0) do
  begin
    AutoexecLines.Delete(StrToInt(PathLineNos.Strings[Index]));
    Index := Index-1;
  end;
  // Then insert new path variable into Autoexec.bat
  AutoexecLines.Insert(FirstPathLineNo, '@PATH='+ResultPath);
  // Delete old Autoexec.bat from disk
  if not DeleteFile('c:\Autoexec.bat') then
    result := mpAutoexecNoWriteAcc;
  Sleep(500);
  // And finally write Autoexec.bat back to disk
  if not (result=mpAutoexecNoWriteAcc) then
    SaveStringToFile('c:\Autoexec.bat', AutoexecLines.Text, false);

  // Free stringlists
  PathLineNos.Free;
  AutoexecLines.Free;
end; // ModifyPath9x

{ Helper function: Modify path on Windows NT, 2000 and XP }
function ModifyPathNT(DirName: string; Method, Scope: integer): integer;
var
  RegRootKey: integer;
  RegSubKeyName: string;
  RegValueName: string;
  OldPath, ResultPath: string;
  OK: boolean;
begin
  // Expect everything to be OK
  result := mpOK;

  // Initialize registry key and value names to reflect if changes should be global or local to current user only
  case Scope of
    psCurrentUser:
      begin
        RegRootKey := HKEY_CURRENT_USER;
        RegSubKeyName := 'Environment';
        RegValueName := 'Path';
      end;
    psAllUsers:
      begin
        RegRootKey := HKEY_LOCAL_MACHINE;
        RegSubKeyName := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
        RegValueName := 'Path';
      end;
  end;

  // Read current path value from registry
  OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, OldPath);
  if not OK then
  begin
    result := mpMissingRights;
    Exit;
  end;

  // Modify path
  ModifyPathString(OldPath, DirName, Method, False, ResultPath);

  // Write new path value to registry
  if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultPath) then
  begin
    result := mpMissingRights;
    Exit;

  end;
end; // ModifyPathNT

{ Main function: Modify path }
function ModifyPath(Path: string; Method, Scope: integer): integer;
begin
  // Check if both add and remove has been specified (= error!)
  if (Method and (pmAddToBeginning or pmAddToEnd) and (pmRemove or pmRemoveSubdirsAlso)) > 0 then
  begin
    result := mpBothAddAndRemove;
    Exit;
  end;

  // Perform directory constant expansion
  Path := ExpandConstantEx(Path, ' ', ' ');

  // Test if Win9x
  if InstallOnThisVersion('4,0','0,0') = irInstall then
    ModifyPath9x(Path, Method);

  // Test if WinNT, 2000 or XP
  if InstallOnThisVersion('0,4','0,0') = irInstall then
    ModifyPathNT(Path, Method, Scope);
end; // ModifyPath


46 - Sample of make different actions in different cases

Article created: 02/05/03 17:10:55
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=46

This sample shows how to run a program and make store registry values depending on which Internet Explorer is installed.

We have 3 different cases:
A action if IE is < 5.01
B action if IE >= 5.01 and < 5.5
C action if IE >= 5.5

It's enough to create a Checkfunction that evaluates all cases and return true if it is true the case requested. If you want install, run or make some INI/Registry action on case A, just call Check: IsCase(A), otherwise if you want to install on case B use Check: IsCase(B)

;
; We have 3 cases
;
; A action if IE is < 5.01
; B action if IE >= 5.01 and < 5.5
; C action if IE >= 5.5

[Setup]
AppName=3IE
AppVerName=3IE
DefaultDirName={pf}\3IE
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=3IE
UpdateUninstallLogAppName=false
DisableProgramGroupPage=true

[Run]
Filename: notepad.exe; Check: IsCase(A); Flags: shellexec
Filename: calc.exe; Check: IsCase(B); Flags: shellexec
Filename: wordpad.exe; Check: IsCase(C); Flags: shellexec

[Registry]
Root: HKCU; Subkey: Software\Mycompany\MyProg; ValueType: string; ValueName: Case; ValueData: A; Check: IsCase(A)
Root: HKCU; Subkey: Software\Mycompany\MyProg; ValueType: string; ValueName: Case; ValueData: B; Check: IsCase(B)
Root: HKCU; Subkey: Software\Mycompany\MyProg; ValueType: string; ValueName: Case; ValueData: C; Check: IsCase(C)

[_ISTool]
EnableISX=true

[Code]

var IEver: String; // store IE version

procedure DecodeVersion( verstr: String; var verint: array of Integer );
var
  i,p: Integer; s: string;
begin
  // initialize array
  verint := [0,0,0,0];
  i := 0;
  while ( (Length(verstr) > 0) and (i < 4) ) do
  begin
      p := pos('.', verstr);
      if p > 0 then
      begin
      if p = 1 then s:= '0' else s:= Copy( verstr, 1, p - 1 );
        verint[i] := StrToInt(s);
        i := i + 1;
        verstr := Copy( verstr, p+1, Length(verstr));
      end
      else
      begin
        verint[i] := StrToInt( verstr );
        verstr := '';
      end;
  end;

end;

function GetIExplorerVersion(): String;
var
  sVersion:  String;
begin
  RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Internet Explorer', 'Version', sVersion );
  Result := sVersion;
end;

// This function compares version string
// return -1 if ver1 < ver2
// return  0 if ver1 = ver2
// return  1 if ver1 > ver2
function CompareVersion( ver1, ver2: String ) : Integer;
var
  verint1, verint2: array of Integer;
  i: integer;
begin

  SetArrayLength( verint1, 4 );
  DecodeVersion( ver1, verint1 );

  SetArrayLength( verint2, 4 );
  DecodeVersion( ver2, verint2 );

  Result := 0; i := 0;
  while ( (Result = 0) and ( i < 4 ) ) do
  begin
      if verint1[i] > verint2[i] then
        Result := 1
      else
      if verint1[i] < verint2[i] then
          Result := -1
        else
          Result := 0;

      i := i + 1;
  end;

end;

function InitializeSetup(): Boolean;
begin

  // Get IE version only once

  IEVer := GetIExplorerVersion();

  Result := true;
end;

function IsCase( mycase: String ) : Boolean;

begin

  // by default, do nothing, install nothing
  Result := false;

  case mycase of

   'A':if CompareVersion( IEVer, '5.01') < 0 then Result := true;
   'B':if ( CompareVersion( IEVer, '5.01') >= 0 ) and ( CompareVersion( IEVer, '5.5') < 0 ) then Result := true;
   'C':if CompareVersion( IEVer, '5.5') >= 0 then Result := true;
  end;

end;


47 - How to store date and time of installation in registry

Article created: 10/05/03 19:27:04
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=47

API call to functions GetSystemTime and GetLocalTime retrieve date and time infos in structure SYSTEMTIME.

Date and time infos, as numbers, are formatted and stored in registry.

;
; ISX 3.0.6
;
;
[Setup]
AppName=DateTimeReg
AppVerName=DateTimeReg
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\DateTimeReg
DisableStartupPrompt=true

[Registry]
Root: HKLM; Subkey: Software\MyCompany\MyProgram; ValueType: string; ValueName: DateTimeInstall; ValueData: {code:GetMyStringNow|''}

[_ISTool]
EnableISX=true

[Code]

type
  TSystemTime = record
    wYear : Word;
    wMonth : Word;
    wDayOfWeek : Word;
    wDay : Word;
    wHour : Word;
    wMinute : Word;
    wSecond : Word;
    wMilliseconds : Word;
  end;

procedure GetSystemTime(var lpSystemTime: TSystemTime); external
'GetSystemTime@kernel32.dll';

procedure GetLocalTime(var lpSystemTime: TSystemTime); external
'GetLocalTime@kernel32.dll';

// variable to store date time infos
var
  st, lt: TSystemTime;

function FormatDateTime( dt: TSystemTime ) : String;
begin

  Result := IntToStr( dt.wDay )+'/'+IntToStr( dt.wMonth )+'/'+IntToStr( dt.wYear ) +
    '  '+IntToStr( dt.wHour )+':'+IntToStr( dt.wMinute )+'.'+IntToStr( dt.wSecond );
end;

function FormatANSI( dt: TSystemTime ) : String;
var s: String;
begin

  Result := IntToStr( dt.wYear );
  s := IntToStr( dt.wMonth ); if length(s) = 1 then s := '0' + s;
  Result := Result + s;
  s := IntToStr( dt.wDay ); if length(s) = 1 then s := '0' + s;
  Result := Result + s;
  s := IntToStr( dt.wHour ); if length(s) = 1 then s := '0' + s;
  Result := Result + s;
  s := IntToStr( dt.wMinute ); if length(s) = 1 then s := '0' + s;
  Result := Result + s;
  s := IntToStr( dt.wSecond ); if length(s) = 1 then s := '0' + s;
  Result := Result + s;

end;

function GetMyStringNow( s : String ) : String;
begin
    Result := FormatANSI( lt );
end;

function InitializeSetup: Boolean;
var
  s, crlf: string;

begin

  crlf := #13#10;

  GetSystemTime(st);
  GetLocalTime(lt);

  s := 'System Time is : ' + crlf +
       FormatDateTime( st ) + crlf + crlf +
       'Local Time is : ' + crlf +
       FormatDateTime( lt );

  // just to show datetime infos
  MsgBox( s ,mbInformation, MB_OK );

  Result := true;
end;


48 - How to set a date time limit for the installer

Article created: 10/05/03 19:32:01
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=48

GetLocalTime function retrieves the current local date and time in SYSTEMTIME structure, it is possibile to compare these data with a known date time structure to stop installation of a program after a date.

;
[Setup]
AppName=DateTimeStop
AppVerName=DateTimeStop
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\DateTimeStop
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

type
  TSystemTime = record
    wYear : Word;
    wMonth : Word;
    wDayOfWeek : Word;
    wDay : Word;
    wHour : Word;
    wMinute : Word;
    wSecond : Word;
    wMilliseconds : Word;
  end;

procedure GetLocalTime(var lpSystemTime: TSystemTime); external
'GetLocalTime@kernel32.dll';

//
// compare dt1 and dt2, return false id dt1 is lower than dt2
// thanks to Bruno Gellweiler that provide a working function :)

function IsOKDateTime( dt1, dt2: TSystemTime ) : Boolean;
begin
    Result := true;
    if dt2.wYear < dt1.wYear then
        result := false
    else if dt2.wYear = dt1.wYear then
        if dt2.wMonth < dt1.wMonth then
            result := false
        else if dt2.wMonth = dt1.wMonth then
            if dt2.wDay < dt1.wDay then
                result := false ;
end;

function InitializeSetup: Boolean;
var
  lt, datelimit: TSystemTime;
  s, crlf: string;

begin

  crlf := #13#10;

  // let's say it is not allowed to install after 1 May 2003
  datelimit.wYear := 2003;
  datelimit.wMonth := 5;
  datelimit.wDay := 1;

  GetLocalTime(lt);

  Result := IsOKDateTime( lt, datelimit );

  s := 'Install time limit, it is not possible to install this program.';

  if not Result then
      // just to show datetime infos
      MsgBox( s , mbError, MB_OK );

end;


49 - How to detect Acrobat Reader search plug-in version

Article created: 23/05/03 22:56:27
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=49

Author: Rob Pitt

Code to detect Acrobat reader search plug-in version.

Function GetAcrobatSearchVersion(): String;
var
  sVersion:  String;
begin
  sVersion := '';
  if (RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe', '', sVersion )) then
  begin
    sVersion := ExtractFileDir(sVersion) + '\plug_ins\search.api';
  end;
  GetVersionNumbersString( sVersion , sVersion );
  Result := sVersion;
end;


50 - How to to retrieve the Printer Driver Directory

Article created: 23/05/03 23:00:35
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=50

This code use API GetPrinterDriverDirectory to retrieve the Printer Driver Directory.

;
;
; ISX 3.0.6.2

[Setup]
AppName=GetPrinterDriver
AppVerName=GetPrinterDriver
Uninstallable=false
DisableDirPage=true
DisableProgramGroupPage=true
DisableStartupPrompt=true
CreateAppDir=false

[_ISTool]
EnableISX=true

[Code]

// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_3y7t.asp

function GetPrinterDriverDirectory(
  pName: PChar;
  pEnvironment: PChar;
  Level: LongInt;
  pDriverDirectory: PChar;
  cbBuf: LongInt;
  var pcbNeened: LongInt  ) : Integer;
external 'GetPrinterDriverDirectoryA@winspool.drv';

{ // API declaration in C
BOOL GetPrinterDriverDirectory(
        LPTSTR pName,             // server name
        LPTSTR pEnvironment,      // environment name
        DWORD Level,              // information level
        LPBYTE pDriverDirectory,  // path buffer
        DWORD cbBuf,              // size of path buffer
        LPDWORD pcbNeeded // bytes received or required
    );
}

function GetLastError( ) : Integer; external 'GetLastError@kernel32.dll';

function InitializeSetup(): Boolean;
var sb: LongInt;
    PrDrvDir : String;
    err: Integer;
begin

    // get length of buffer
    err := GetPrinterDriverDirectory( null, null, 1, null, 0, sb) ;

    //MsgBox( 'Size buffer = ' + Inttostr( sb ) ,0 ,0 );

    PrDrvDir := StringOfChar( ' ', sb+1 );

    err := GetPrinterDriverDirectory( null, null, 1, PrDrvDir, sb, sb) ;

    PrDrvDir := CastIntegerToString( CastStringToInteger(PrDrvDir) );

    MsgBox( 'Printer Driver Files are in ' + PrDrvDir , 0, 0 );

    Result := false;
end;


51 - How to remove a web share at uninstall time

Article created: 13/06/03 22:45:27
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=51

Authors: Vincenzo Giordano, Bo Berglund

To remove a webshare from IIS at uninstall time, it is necessary to install a vbscript and call it at unistall time.
Share name is first parameter or script, so it is possible to call it more times.

;
;
; ISX 3.0.6.2

[Setup]
AppName=DelWebDir
AppVerName=DelWebDir
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\DelWebDir
DisableStartupPrompt=true
OutputBaseFilename=DelWebDir
AllowUNCPath=false
Compression=bzip
ShowTasksTreeLines=false

[_ISTool]
EnableISX=true

[Files]
;install script file to run at uninstall time
Source: delvdir.vbs; DestDir: {app}

[UninstallRun]
Filename: wscript.exe; Parameters: """{app}\delvdir.vbs"" ""MyShare"""; WorkingDir: {app}; Flags: runminimized
delvdir.vbs

' This program removes a virtual directory from IIS if it exists.
' The directory must be specified on the command line
Dim strVDName  'IIS Virtual Directory Name
Dim objIIS     'ADSI IIS Object
Dim objRoot    'ADSI Root Object
Dim objVdir    'ADSI Virtual Dir Object

If WScript.Arguments(0) <> "" Then

  'get dir name from command line
  strVDName = WScript.Arguments(0)

  On Error Resume Next
  'Check if directory is already registered,
  'this will produce an error if it is not
  Set objIIS = GetObject("IIS://localhost/W3SVC/1/Root/" & strVDName)

  If Err.Number = 0 Then
    'No error so directory registration exists, we need to remove it
    Set objIIS = Nothing
    Set objIIS = GetObject( "IIS://localhost/W3SVC/1" )
    Set objRoot = objIIS.GetObject( "IIsWebVirtualDir","Root" )
    Set objVdir = objRoot.Delete( "IIsWebVirtualDir", strVDName)

  End If
End If


52 - Library to access ODBC functions

Article created: 13/06/03 21:10:38
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=52

Library useful to access ODBC funtions.

[Code]

const
    SQL_SUCCESS 				=	0;
    SQL_SUCCESS_WITH_INFO		=	1;
    SQL_NO_DATA					= 100;

    SQL_HANDLE_ENV	=	1;
    SQL_HANDLE_DBC	=	2;
    SQL_HANDLE_STMT	=	3;
    SQL_HANDLE_DESC	=	4;

    SQL_NULL_HENV		=	0;
    SQL_NULL_HDBC		=	0;
    SQL_NULL_HSTMT	=	0;
    SQL_NULL_HDESC	=	0;
    SQL_NULL_HANDLE	=	0;

const SQL_DRIVER_NOPROMPT = 0;
const SQL_DRIVER_COMPLETE = 1;
const SQL_DRIVER_PROMPT = 2;
const SQL_DRIVER_COMPLETE_REQUIRED = 3;

const SQL_FETCH_NEXT = 1;
const SQL_FETCH_FIRST_USER = 31;
const SQL_FETCH_FIRST_SYSTEM = 32;

const
    SQL_ATTR_ODBC_VERSION = 200;
    SQL_OV_ODBC3 = 3;
    SQL_NTS = ( - 3);

type SQLSMALLINT = Smallint;
type SQLRETURN = SQLSMALLINT;
type SQLHANDLE = LongInt;
type SQLHENV = SQLHANDLE;
type SQLHDBC = SQLHANDLE;
type SQLINTEGER = LongInt;
type SQLPOINTER = SQLHANDLE;  // Pointer
type PSQLCHAR = PChar;
type SQLUSMALLINT = Word;

const BUFSIZE = 500;

// ------
// SQLDriverConnect
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21dpr_3.asp
// ------
{
SQLRETURN SQLDriverConnect(
     SQLHDBC     ConnectionHandle,
     SQLHWND     WindowHandle,
     SQLCHAR *     InConnectionString,
     SQLSMALLINT     StringLength1,
     SQLCHAR *     OutConnectionString,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     StringLength2Ptr,
     SQLUSMALLINT     DriverCompletion);
}

function SQLDriverConnect(
  hdbc: SQLHDBC;
  hwnd: Integer;
  szCsin: PChar;
  szCLen: SQLSMALLINT;
  szCsout: PChar;
  cbCSMax: SQLSMALLINT;
  var cbCsOut: SQLSMALLINT;
  f: Integer
):SQLRETURN;
external 'SQLDriverConnect@ODBC32.DLL stdcall';

// ------
// SQLAllocHandle
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21bpr_2.asp
// ------
{
SQLRETURN SQLAllocHandle(
     SQLSMALLINT     HandleType,
     SQLHANDLE     InputHandle,
     SQLHANDLE *     OutputHandlePtr);
}

function SQLAllocHandle(
    HandleType: SQLSMALLINT;
    InputHandle: SQLHANDLE;
    var OutputHandlePtr: SQLHANDLE
    ): SQLRETURN;
external 'SQLAllocHandle@ODBC32.DLL stdcall';

// ------
// SQLFreeHandle
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21fpr_3.asp
// ------
{
SQLRETURN SQLFreeHandle(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle);
}

function SQLFreeHandle(
    HandleType: SQLSMALLINT;
    Handle: SQLHANDLE
    ): SQLRETURN;
external 'SQLFreeHandle@ODBC32.DLL stdcall';

// ------
// SQLSetEnvAttr
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21jpr_8.asp
// ------
{
SQLRETURN SQLSetEnvAttr(
     SQLHENV     EnvironmentHandle,
     SQLINTEGER     Attribute,
     SQLPOINTER     ValuePtr,
     SQLINTEGER     StringLength);
}

function SQLSetEnvAttr(
  EnvironmentHandle: SQLHENV;
  Attribute: SQLINTEGER;
  Value: SQLPOINTER;
  StringLength: SQLINTEGER
):SQLRETURN;
external 'SQLSetEnvAttr@ODBC32.DLL stdcall';

// ------
// SQLDisconnect
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21dpr_2.asp
// ------
{
SQLRETURN SQLDisconnect(
     SQLHDBC     ConnectionHandle);

}

function SQLDisconnect(
  ConnectionHandle: SQLHDBC
):SQLRETURN;
external 'SQLDisconnect@ODBC32.DLL stdcall';

// ------
// SQLGetDiagRec
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21gpr_4.asp
// ------
{
SQLRETURN SQLGetDiagRec(
     SQLSMALLINT     HandleType,
     SQLHANDLE     Handle,
     SQLSMALLINT     RecNumber,
     SQLCHAR *     Sqlstate,
     SQLINTEGER *     NativeErrorPtr,
     SQLCHAR *     MessageText,
     SQLSMALLINT     BufferLength,
     SQLSMALLINT *     TextLengthPtr);
}

function SQLGetDiagRec(
  HandleType: SQLSMALLINT;
  Handle: SQLHANDLE;
  RecNumber: SQLSMALLINT;
  Sqlstate: PSQLCHAR;
  var NativeError: SQLINTEGER;
  MessageText: PSQLCHAR;
  BufferLength: SQLSMALLINT;
  var TextLength: SQLSMALLINT
):SQLRETURN;
external 'SQLGetDiagRec@ODBC32.DLL stdcall';

// ------
// SQLDataSources
// ------
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odch21cpr_4.asp
// ------
{
SQLRETURN SQLDataSources(
     SQLHENV     EnvironmentHandle,
     SQLUSMALLINT     Direction,
     SQLCHAR *     ServerName,
     SQLSMALLINT     BufferLength1,
     SQLSMALLINT *     NameLength1Ptr,
     SQLCHAR *     Description,
     SQLSMALLINT     BufferLength2,
     SQLSMALLINT *     NameLength2Ptr);
}

function SQLDataSources(
  EnvironmentHandle: SQLHENV;
  Direction: SQLUSMALLINT;
  ServerName: PSQLCHAR;
  BufferLength1: SQLSMALLINT;
  // NameLength1: PSQLSMALLINT;
  var NameLength1: SQLSMALLINT;
  Description: PSQLCHAR;
  BufferLength2: SQLSMALLINT;
  // NameLength2: PSQLSMALLINT
  var NameLength2: SQLSMALLINT
):SQLRETURN;
external 'SQLDataSources@ODBC32.DLL stdcall';

function IsSQLRetOk( sqlret: SQLRETURN ) : Boolean;
begin
    Result := ( sqlret = SQL_SUCCESS ) or ( sqlret = SQL_SUCCESS_WITH_INFO );
end;

function GetODBCMsg( HandleType : SQLSMALLINT; Handle : SQLHANDLE ): String;
var
    sqlret : SQLRETURN;
    recnum : SQLSMALLINT;
    sqlstate: String;
    errmsg: String;
    nativeerr: SQLINTEGER;
    textlen: SQLSMALLINT;

begin

    recnum := 1;

    sqlstate := StringOfChar( ' ', 6 );
    errmsg := StringOfChar( ' ', BUFSIZE );

    sqlret := SQLGetDiagRec( HandleType,
                            Handle,
                            recnum,
                            sqlstate,
                            nativeerr,
                            errmsg,
                            BUFSIZE - 1,
                            textlen );

    sqlstate := CastIntegerToString( CastStringToInteger(sqlstate) );
    errmsg := CastIntegerToString( CastStringToInteger(errmsg) );

    if IsSQLRetOk( sqlret ) then
        Result := sqlstate + ' - ' + errmsg
    else
        Result := 'Error retriving error message!';

end;


53 - How to test a SQL Server connection using ODBC driver

Article created: 13/06/03 21:14:45
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=53

If SQL Server ODBC driver is installed, it is possible ask connection parameters and test if these params are good to connect to Sql Server.

Same code is good to test MSDE connection also, and changing driver string is possible to test any database, it's only necessary the ODBC driver and connection paramters.

To compile this sample you need ODBC function library.

See also:

How to test a SQL Server connection with ADO
How to test a SQL Server connection using ODBC driver for IS 5

;
; ISX 3.0.6.2

[Setup]
AppName=ODBCtest
AppVerName=ODBCtest
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\ODBCtest
DisableStartupPrompt=true
CreateAppDir=false
OutputBaseFilename=TestODBC
Compression=bzip
AllowUNCPath=false

[_ISTool]
EnableISX=true

[Code]

// include const and function declarations
#include "odbclib.iss"

var

  // Custom pages controls
  testbut: TButton;
  edServer, edUser, edPassword: TEdit;
  lbServer, lbUser, lbPassword: TLabel;

  // variables
  myserver, myuser, mypwd: String;

//	Parameters for SQL Server
//	DRIVER={SQL Server}
//	SERVER=servername
//	UID=username
//	PWD=password
//	DATABASE=dbname (optional)
//	WSID=workstation id (optional usually computer name)
//	APP=application (optional)
//	NETWORK=Dbmssocn  (optional)
//	ADDRESS=server,port (optional)

{ Protocols
TCP/IP Windows Sockets	Dbmssocn.dll
Named pipes 			Dbnmpntw.dll
Multiprotocol 			Dbmsrpcn.dll
Novell SPX/IPX 			Dbmsspxn.dll
Banyan Vines			Dbmsvinn.dll
DECNet 					Dbmsdecn.dll
AppleTalk 				Dbmsadsn.dll
}

//
// connect to server using usr and pwd
//
function ConnectServer( server, usr, pwd : String ) : Boolean;
var	envH :SQLHANDLE ;	// env handle
    conH :SQLHANDLE;	// connection handle
    sqlret : SQLRETURN;
    hwnd : LongInt;	   // window wizard handle
    constrIN, constrOUT: String;
    sizeout : SQLSMALLINT;
    msg: String;
begin

  hwnd := StrToInt(ExpandConstant('{wizardhwnd}'));
  envH := SQL_NULL_HENV;
  conH := SQL_NULL_HDBC;
  Result := true;

  sqlret := SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, envH );
  if sqlret = SQL_SUCCESS then
  begin

    sqlret := SQLSetEnvAttr( envH, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0 );
      if sqlret = SQL_SUCCESS then
      begin

        sqlret := SQLAllocHandle( SQL_HANDLE_DBC, envH, conH );
        if sqlret = SQL_SUCCESS then
        begin
        constrIN := 'DRIVER={SQL Server};SERVER='+server+';UID='+usr+';PWD='+pwd+';APP=ISX Test;';
        // if it's necessary connect via tcp/ip
        // constrIN := constrIN + 'NETWORK=Dbmssocn;ADDRESS='+server+',1433';
        constrOUT := StringOfChar( ' ', BUFSIZE );

        // connessione
        sqlret := SQLDriverConnect( conH,
                  hwnd,
                  constrIN,
                  Length(constrIN),
                  constrOUT,
                  BUFSIZE - 1,
                  sizeout,
                  SQL_DRIVER_NOPROMPT  );

        constrOUT := CastIntegerToString( CastStringToInteger(constrOUT) );

        msg := #13#10 + #13#10 + GetODBCMsg( SQL_HANDLE_DBC, conH );

        if IsSQLRetOk( sqlret ) then
        begin

          MsgBox( 'Connection string is: ' + #13#10 + #13#10 + constrOUT + msg, mbInformation ,MB_OK );
          sqlret := SQLDisconnect( conH );
        end
        else begin
          MsgBox( 'Connection failed!' + #13#10 + #13#10 + constrIN + msg, mbError , MB_OK );
          sqlret := SQLFreeHandle( SQL_HANDLE_DBC, conH );
          Result := false;
          end;
        end
      else begin
        msg := GetODBCMsg( SQL_HANDLE_DBC, conH );
        MsgBox( 'Error creating CON handle:' + #13#10 + #13#10 + msg, mbError , MB_OK );
        Result := false;
        end;

    end
    else begin
      msg := GetODBCMsg( SQL_HANDLE_ENV, envH );
      MsgBox( 'Error setting ODBC version:' + #13#10 + #13#10 + msg, mbError , MB_OK );
      Result := false;
    end

    // anyway release ENV handle
    sqlret := SQLFreeHandle( SQL_HANDLE_ENV, envH );

  end
  else begin
    msg := GetODBCMsg( SQL_HANDLE_ENV, envH );
    MsgBox( 'Error creating ENV handle:' + #13#10 + #13#10 + msg, mbError , MB_OK );
    Result := false;
  end;

end;

function InitializeSetup() : boolean;
begin

  myserver := 'ServerName';
  myuser := 'username';
  mypwd := '';

  Result := True;
end;

procedure TestOnClick(Sender: TObject);
begin
    WizardForm.Enabled := false;

    myserver := edServer.Text;
    myuser := edUser.Text;
    mypwd := edPassword.Text;

    ConnectServer( myserver, myuser , mypwd  );
    WizardForm.Enabled := true;
end;

function InformazioniDBServer( BackClicked: Boolean): Boolean;
var

  Next: Boolean;
begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('Connection to Database Server');
    ScriptDlgPageSetSubCaption1('Paramters to test Database Server connection');

    lbServer := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbServer.Top := 0;
    lbServer.Caption := 'Server Name:';
    lbServer.AutoSize := True;
    lbServer.Parent := WizardForm.ScriptDlgPanel;

    edServer := TEdit.Create(WizardForm.ScriptDlgPanel);
    edServer.Top := lbServer.Top + lbServer.Height + 8;
    edServer.Text := myserver;
    edServer.Width := WizardForm.ScriptDlgPanel.Width;
    edServer.Parent := WizardForm.ScriptDlgPanel;
    edServer.AutoSelect := true;

    lbUser := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbUser.Top := edServer.Top + edServer.Height + 8;
    lbUser.Caption := 'Username:';
    lbUser.AutoSize := True;
    lbUser.Parent := WizardForm.ScriptDlgPanel;

    edUser := TEdit.Create(WizardForm.ScriptDlgPanel);
    edUser.Top := lbUser.Top + lbUser.Height + 8;
    edUser.Text := myuser;
    edUser.Width := WizardForm.ScriptDlgPanel.Width;
    edUser.Parent := WizardForm.ScriptDlgPanel;

    lbPassword := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbPassword.Top := edUser.Top + edUser.Height + 8;
    lbPassword.Caption := 'Password:';
    lbPassword.AutoSize := True;
    lbPassword.Parent := WizardForm.ScriptDlgPanel;

    edPassword := TEdit.Create(WizardForm.ScriptDlgPanel);
    edPassword.Top := lbPassword.Top + lbPassword.Height + 8;
    edPassword.Text := mypwd;
    edPassword.Width := WizardForm.ScriptDlgPanel.Width;
    edPassword.Parent := WizardForm.ScriptDlgPanel;
    edPassword.PasswordChar := '*';

    testbut := TButton.Create(WizardForm.ScriptDlgPanel);
    testbut.Top := edPassword.Top + edPassword.Height + 8 + 8;
    testbut.Caption := 'Test';
    testbut.Width := WizardForm.ScriptDlgPanel.Width / 3 ;
    testbut.Parent := WizardForm.ScriptDlgPanel;
    testbut.OnClick := @TestOnClick;

    Next := ScriptDlgPageProcessCustom();

    myserver := edServer.Text;
    myuser := edUser.Text;
    mypwd := edPassword.Text;

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    // lo mostro solo se non sto aggiornando
    Result := InformazioniDBServer( BackClicked )
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


54 - How to list User and Sytem DSN

Article created: 13/06/03 21:30:09
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=54

In a custom page there is a listbox and two buttons, clicking on a button, a function fill listbox with name and driver of all user or system DSN installed on the PC.

To compile this sample you need ODBC function library.

;
; ISX 3.0.6.2

[Setup]
AppName=DSNList
AppVerName=DSNList
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\DSNList
DisableStartupPrompt=true
CreateAppDir=false
OutputBaseFilename=DSNList
Compression=bzip
AllowUNCPath=false

[_ISTool]
EnableISX=true

[Code]

#include "odbclib.iss"

var

  // custom pages controls
  getuser, getsys: TButton;
  lbNames : TListBox;
  lUser:TLabel;

Procedure GetDSNList( listtype: SQLUSMALLINT );
var	envH :SQLHANDLE ;
    sqlret : SQLRETURN;
    direction : SQLUSMALLINT;
    msg: String;
    dsnname, dsndriver: String;
    namelen, driverlen :SQLSMALLINT;
begin

    envH := SQL_NULL_HENV;

    sqlret := SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, envH );
    if sqlret = SQL_SUCCESS then
    begin

        sqlret := SQLSetEnvAttr( envH, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0 );

        direction := listtype;

        dsnname := StringOfChar( ' ', 100 );
        dsndriver := StringOfChar( ' ', 100 );

        while (
                SQLDataSources(
                      envH, direction,
                      dsnname, 99, namelen,
                      dsndriver, 99, driverlen ) = SQL_SUCCESS
              ) do
        begin
            direction := SQL_FETCH_NEXT;

              dsnname := CastIntegerToString( CastStringToInteger(dsnname) );
              dsndriver := CastIntegerToString( CastStringToInteger(dsndriver) );

            lbNames.Items.Add( dsnname + ' <--> ' + dsndriver );

            dsnname := StringOfChar( ' ', 100 );
            dsndriver := StringOfChar( ' ', 100 );

        end;

          sqlret := SQLFreeHandle( SQL_HANDLE_ENV, envH );

    end
     else begin
          msg := GetODBCMsg( SQL_HANDLE_ENV, envH );
        MsgBox( 'Error creating ENV handle:' + #13#10 + #13#10 + msg, mbError , MB_OK );
      end;

end;

function InitializeSetup() : boolean;
begin
    Result := True;
end;

procedure GetUserDSNList(Sender: TObject);
begin

    lUser.Caption := 'User DSN:';
    lbNames.Items.Clear;

    GetDSNList( SQL_FETCH_FIRST_USER );

end;

procedure GetSystemDSNList(Sender: TObject);
begin

    lUser.Caption := 'System DSN:';
    lbNames.Items.Clear;

    GetDSNList( SQL_FETCH_FIRST_SYSTEM );

end;

const SPACE_BETWEEN = 20;

function DSNListPage( BackClicked: Boolean): Boolean;
var

  Next: Boolean;
begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('ODBC DSN');
    ScriptDlgPageSetSubCaption1('List of User or SYSTEM DSN');

    lUser := TLabel.Create(WizardForm.ScriptDlgPanel);
    lUser.Caption := 'Name:';
    lUser.AutoSize := True;
    lUser.Parent := WizardForm.ScriptDlgPanel;

    lbNames := TListBox.Create(WizardForm.ScriptDlgPanel);
    lbNames.Top := lUser.Top + lUser.Height + 8;
    lbNames.Width := WizardForm.ScriptDlgPanel.Width;
    lbNames.Parent := WizardForm.ScriptDlgPanel;

    getuser := TButton.Create(WizardForm.ScriptDlgPanel);
    getuser.Top := WizardForm.ScriptDlgPanel.Height - getuser.Height - 10;
    getuser.Caption := 'Get USER DSN';
    getuser.Width := ( WizardForm.ScriptDlgPanel.Width - SPACE_BETWEEN ) / 2 ;
    getuser.Parent := WizardForm.ScriptDlgPanel;
    getuser.OnClick := @GetUserDSNList;

    getsys := TButton.Create(WizardForm.ScriptDlgPanel);
    getsys.Top := WizardForm.ScriptDlgPanel.Height - getuser.Height - 10;
    getsys.Left := ( WizardForm.ScriptDlgPanel.Width - SPACE_BETWEEN ) / 2 + SPACE_BETWEEN;
    getsys.Caption := 'Get SYSTEM DSN';
    getsys.Width := getuser.Width ;
    getsys.Parent := WizardForm.ScriptDlgPanel;
    getsys.OnClick := @GetSystemDSNList;

    Next := ScriptDlgPageProcessCustom();

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    Result := DSNListPage( BackClicked )
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


55 - How to install Windows Media Player 9

Article created: 25/06/03 21:20:33
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=55

Author: Ed Eichman

This script shows how to install Windows Media Player 9.
(can easily be adapted for other versions).

;Code on installing the WMPlayer 9
;(can easily be adapted for other versions).
; Script generated June of 2003 by Rocinante Software S.L.
; Ed Eichman

[Setup]
AppName=WMPlayer9
AppVerName=WMPlayer9
DefaultDirName={sd}\WMPlayer9
DisableStartupPrompt=true
Uninstallable=false
DisableProgramGroupPage=true

;---- IMPORTANT!!!!---- IMPORTANT!!!!---- IMPORTANT!!!!---- IMPORTANT!!!!---- IMPORTANT!!!!---- IMPORTANT!!!!=
; According to the readme for MPSetup.exe, "You must be logged on as an administrator or a member of the=
;Administrators group to install Windows Media Player 9 Series on computers running Windows 2000, Windows XP=
;Home Edition, or Windows XP Professional. For more information about user accounts and groups, see Windows Help."=
PrivilegesRequired=admin

[Files]
;Obtain MPSetup.exe from http: //www.microsoft.com/windows/windowsmedia/download/
;You also need to get permission from MS for redistributing the file
Source: MPSetup.exe; DestDir: {tmp}; Flags: deleteafterinstall; Check: WMP9Needed

[Run]
;Run silently as shown in article "Redistributing Windows Media 9 Series Components":
;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/RedisWMedC.asp:
;Note: This installer works with all WMPlayer9 system versions, including XP.:
;Although there is a separate installer for WMPlayer9XP, the installer for the other windows:
;version may be installed on XP. See
;http: //msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/RedisWMedC.asp:
;where they say...
;The MPSetup.EXE package is supported on Windows 98 Second Edition, Windows Millennium Edition,:
;Windows 2000, and Windows XP. MPSetupXP.exe is optimized for and will run only on Windows XP.:
;There is no functional difference between installing MPSetup.exe and MPSetupXP on a computer:
;running Windows XP.
;NOTE: I tried this installer on XP, and it works fine.
Filename: {tmp}\MPSetup.exe; Parameters: """""""/q:A /c:""""""""setup_wm.exe /Q:A /R:N"" /P: ""#e""""""""""""; StatusMsg: """"Installing Windows Media Player 9...""""; Check:""WMP9Needed:"

[Code]
var
  bNeedsReboot: Boolean;

//------- --------- --------- --------- --------- --------- --------- --------- ---------
//Adapted from article "How to detect DirectX version " at
//http://www13.brinkster.com/vincenzog/isxart.asp?idart=7
procedure DecodeVersion( verstr: String; var verint: array of Integer );
var
  i,p: Integer; s: string;
  cSep: char;
begin
  //A little help for the Europeans
  cSep := '.';
  if (0 = pos ('.', verstr)) and (0 <> pos (',', verstr)) then begin
    cSep := ',';
  end;
  // initialize array
  verint := [0,0,0,0];
  i := 0;
  while ( (Length(verstr) > 0) and (i < 4) ) do
  begin
        p := pos(cSep, verstr);
        if p > 0 then
        begin
      if p = 1 then s:= '0' else s:= Copy( verstr, 1, p - 1 );
          verint[i] := StrToInt(s);
          i := i + 1;
          verstr := Copy( verstr, p+1, Length(verstr));
        end
        else
        begin
          verint[i] := StrToInt( verstr );
          verstr := '';
        end;
  end;

end;

//------- ------- ------- ------- ------- ------- ------- ------- ------- ------- ------- ------- -------
//-----------------DETERMINING THE VERSION OF WMPlayer INSTALLED-----------------------------------------
//From "Redistributing Windows Media 9 Series Components" on MSDN
//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/RedisWMedC.asp
//In particular, the part where they say...
//Redistributing Windows Media Player 9 Series Software
//To include Windows Media Player in your application, you use the Window Media Player installation
//package, MPSetup.exe or MPSetupXP.exe. The MPSetup.EXE package is supported on Windows 98 Second
//Edition, Windows Millennium Edition, Windows 2000, and Windows XP. MPSetupXP.exe is optimized for
//and will run only on Windows XP. There is no functional difference between installing MPSetup.exe
//and MPSetupXP on a computer running Windows XP.
//Detecting Windows Media Player from an Application
//You can determine what version of Windows Media Player is installed by checking the following
//registry key:
//HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components
//For Windows Media Player 6.4, look at key {22d6f312-b0f6-11d0-94ab-0080c74c7e95}.
//For Windows Media Player 7, Windows Media Player for Windows XP, or Windows Media Player 9 Series;
//look at key {6BF52A52-394A-11d3-B153-00C04F79FAA6}.
//Under either of these keys, if the IsInstalled DWORD value is set to 0x1, you can safely use
//the "Version" string value as the version of Windows Media Player that is installed.
function WMP9Needed (): Boolean;
 var
   dwIsInstalled:  Cardinal;
   strVersion: String;
   lCurrInstalledVersion: Longint;
   verint: array of Integer;
begin
  Result := TRUE;
  RegQueryDWordValue(HKLM, 'Software\Microsoft\Active Setup\Installed Components\{6BF52A52-394A-11d3-B153-00C04F79FAA6}',
      'IsInstalled', dwIsInstalled);
   if 1 = dwIsInstalled then begin
    RegQueryStringValue(HKLM, 'Software\Microsoft\Active Setup\Installed Components\{6BF52A52-394A-11d3-B153-00C04F79FAA6}',
      'Version', strVersion);
    //MsgBox (strVersion, mbInformation, MB_OK);
    SetArrayLength (verint, 4);
    DecodeVersion (strVersion, verint);
    if verint[0] >= 9 then begin
      //MsgBox ('Its 9', mbInformation, MB_OK);
      Result := FALSE;
    end;
   end;
end;

//------- --------- --------- --------- --------- --------- --------- ------ --- ---------
function InitializeSetup(): Boolean;
begin
  //Make sure this gets checked AT THE BEGINNING.
  //Hmmm, my PASCAL skills are rusty - what is the best way to guarantee bNeedsReboot
  //is inited BEFORE any of the callbacks are called. Please let me know at
  //eeichman
  //--at--
  //rocinantesoftware.com

  bNeedsReboot := WMP9Needed;
  Result := TRUE;
end;

//------- --------- --------- --------- --------- --------- --------- --------- ---------
function NeedRestart(): Boolean;
begin
  //I'm not 100% sure that a reboot is needed if installing this, but what the hell.
  Result := bNeedsReboot or WMP9Needed;
end;


56 - Add a button to License page to print it

Article created: 25/06/03 21:40:33
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=56

In the Accept License Page it is possible to add a button, user can click on it and print license text on the default printer.
License is printed using WordPad, so it is possible to print TXT and RTF license text.

Since version 5.0.5 changed function ExtractTemporaryFile and since version 5.0.1 function InstExec was replaced b function Exec so for version 5 of Inno Setup see Add a button to License page to print it for IS 5.

[Setup]
AppName=PrintLic
AppVerName=PrintLic
DefaultDirName={pf}\PrintLic
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=PrintLic
CreateAppDir=false
DisableProgramGroupPage=true
LicenseFile=MyLicense.rtf

[Files]
Source: MyLicense.rtf; Flags: dontcopy

[Code]

// Print license
procedure PrintButtonClick(Sender: TObject);

var WordpadLoc: String;
    RetCode: Integer;
begin

  if MsgBox( 'Do you want to print License to default printer ?', mbConfirmation, MB_YESNO ) = IDYES then
  begin

    // get WordPad location
    RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WORDPAD.EXE',
      '', WordpadLoc);

    // on NT/2000 it's a REG_EXPAND_SZ, so expand constant ProgramFiles
    StringChange( WordpadLoc, '%ProgramFiles%', ExpandConstant('{pf}') );
    // remove " at begin and end pf string
    StringChange( WordpadLoc, '"', '');

    // extract License file
    if ExtractTemporaryFile('MyLicense.rtf') then
    begin

    if not InstExec( WordPadLoc, ' /p ' + ExpandConstant('{tmp}\MyLicense.rtf') ,
         ExpandConstant('{tmp}'), false, false,  SW_HIDE, RetCode ) then
      MsgBox('Problems printing License file !' + IntToStr(RetCode), mbError, mb_Ok)

    end
    else
      MsgBox('Problems extracting License file !', mbError, mb_Ok);
  end;

end;

procedure CurPageChanged(CurPage: Integer);
var PrintButton: TButton;
begin
  if CurPage = wpLicense then
  begin

    PrintButton := TButton.Create(WizardForm.ScriptDlgPanel);
    PrintButton.Caption := '&Print License';
    PrintButton.Left := WizardForm.LicenseMemo.Left +
                        WizardForm.LicenseMemo.Width - PrintButton.Width;
    PrintButton.Top := WizardForm.LicenseMemo.Top +
                        WizardForm.LicenseMemo.Height + 16;
    PrintButton.OnClick := @PrintButtonClick;
    PrintButton.Parent := WizardForm.LicenseAcceptedRadio.Parent;

  end;
end;


57 - How to show format media dialog

Article created: 16/09/03 23:20:45
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=57

This sample code shows how to show format media dialog, calling function SHFormatDrive.

[Setup]
AppName=FormatFloppy
AppVerName=FormatFloppy
AllowRootDirectory=true
DirExistsWarning=no
DefaultDirName=C:\
AllowUNCPath=false
DisableProgramGroupPage=true
DisableStartupPrompt=true
Uninstallable=false
OutputBaseFilename=FormatFloppy
CreateAppDir=false

[Code]

Const
  SHFMT_DRV_A = 0;
  SHFMT_DRV_B = 1;
  SHFMT_ID_DEFAULT = $FFFF;
  SHFMT_OPT_QUICKFORMAT = 0;
  SHFMT_OPT_FULLFORMAT = 1;
  SHFMT_OPT_SYSONLY = 2;
  SHFMT_ERROR = -1;
  SHFMT_CANCEL = -2;
  SHFMT_NOFORMAT = -3;

function SHFormatDrive(hWnd : Integer; Drive : Word; fmtID : Word; Options : Word) : Longint;
external 'SHFormatDrive@Shell32.dll stdcall';

function NextButtonClick(CurPage: Integer): Boolean;
var
    FmtRes : LongInt;
begin
    Result := true;
    if CurPage = wpWelcome then
    begin

      Result := false;
      if not Result then
      begin

          FmtRes := ShFormatDrive( StrToInt(ExpandConstant('{wizardhwnd}')),
                        SHFMT_DRV_A,
                        SHFMT_ID_DEFAULT,
                        SHFMT_OPT_QUICKFORMAT);

        // ok only if floppy was formatted
        Result := FmtRes = 0;

      end;
    end

end;


58 - Replace a line in a text file with comment check

Article created: 16/09/03 23:42:58
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=58

Author: Silvio Iaccarino

Using function of How to replace a line in a text file using ReplaceLine() can be a problem because there is no comment check.

If you have this situation:

file config.nt
---------------------------
rem device = c:\winnt\system32\ramdisk.sys
device = c:\myramdisk\ramdisk.sys
---------------------------
and you want to replace the ramdisk driver with ReplaceLine(), you get this result:
---------------------------
device = c:\mynewramdisk\ramdisk.sys
device = c:\mynewramdisk\ramdisk.sys
---------------------------

so there has to be a comment check like this:

[Code]

function ReplaceLine(strFilename, strFind, strNewLine,commentType: string):
boolean ;
var
  i                     : longint;
  strLine               : TArrayOfString;
  commentSize   : longint;
  arraySize             : longint;
  fileChanged   : boolean;
  strTemp               : string;
begin
  // Load textfile into string array
  LoadStringsFromFile(strFilename, strLine);

  fileChanged   := false;
  commentSize   := Length(commentType);
  arraySize             := GetArrayLength(strLine)-1;

  // Search trough all textlines for given text
  for i := 0 to arraySize do begin
    // Overwrite textline when text searched for is part of it
    if Pos(strFind,strLine[i]) > 0 then begin
        // does this line start with a comment ?
        if commentSize > 0 then begin
          strTemp := TrimLeft(strLine[i]);
          // only replace if line does not start with a comment
          if CompareText(Copy(strTemp,1,commentSize),commentType) <> 0 then
          begin
          strLine[i] := strNewLine;
          fileChanged := true
          end
        end
        else begin
        strLine[i] := strNewLine;
        fileChanged := true
        end
    end
  end;

  // Save string array to textfile (overwrite, no append!)
  if fileChanged = true then
        SaveStringsToFile(strFilename, strLine, false);

  Result := true
end;


59 - How to create a drive map using the network connection dialog box

Article created: 16/09/03 23:55:12
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=59

Author: Lambert Antonio

This script shows how to create a drive map using the network connection dialog box.

; -- MapDrive1.iss --
;
; This script shows how to create a drive map using the network connection dialog box.
; - Lambert Antonio.

[Setup]
AppName=ISX [Map Drive]
AppVerName=ISX [Map Drive] 1.0
DefaultDirName=C:\ISX

[Files]

[Code]
const
  RESOURCETYPE_DISK = 1;

function WNetConnectionDialog(hWnd: Integer; dwType: Integer): Integer;
external 'WNetConnectionDialog@mpr.dll stdcall';

function NextButtonClick(CurPage: Integer): Boolean;
begin
  if CurPage = 0 then
    WNetConnectionDialog(0, RESOURCETYPE_DISK);
  Result := True;
end;


60 - Files to install SQL-DMO for SQL Server 2000

Article created: 17/09/03 0:01:20
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=60

This is what it's necessary to install SQL-DMO for SQL Server 2000 on client PC.

See article How to distribute and how to install SQL-DMO for SQL Server 2000

There is an issue regarding registrant of sqldmo.dll, that on some Win98 PC can fail, due to atl.dll missing, so it's necessary to install them, you can get them from the VCREDIST File that you can download from:

259403 - Vcredist.exe Installs the Latest Run-Time Components for Visual C++ Applications

All SQL-DMO files are taken from your installation, I suggetst to install at least SP3 of SQL SERVER 2000.
Other links:

248241- INF: Enabling SQL DMO Clients Without Installing the Client Side Utilities
258157 - HOWTO: Deploy SQL Distributed Management Objects with the Visual Basic Package and Deployment Wizard

[Files]

; To succesfully register sqldmo.dll it is necessary install ATL
Source: Files\dmo\sys32\atla.dll; DestDir: {sys}; Flags: uninsneveruninstall regserver; DestName: atl.dll; MinVersion: 1,0; Components: sqldmo
Source: Files\dmo\sys32\atlu.dll; DestDir: {sys}; Flags: uninsneveruninstall regserver; DestName: atl.dll; MinVersion: 0,1; Components: sqldmo

Source: Files\dmo\sys32\sqlunirl.dll; DestDir: {sys}; Flags: uninsneveruninstall; Components: sqldmo
Source: Files\dmo\Binn\sqlsvc.dll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn; Flags: uninsneveruninstall; Components: sqldmo
Source: Files\dmo\Binn\sqlresld.dll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn; Flags: uninsneveruninstall; Components: sqldmo
Source: Files\dmo\Binn\sqldmo.dll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn; Flags: uninsneveruninstall regserver; Components: sqldmo
Source: Files\dmo\Binn\w95scm.dll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn; Flags: uninsneveruninstall; Components: sqldmo
; English Resources
Source: Files\dmo\Resources\1033\Sqldmo.rll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn\Resources\1033; Flags: uninsneveruninstall; Components: sqldmo
Source: Files\dmo\Resources\1033\sqlsvc.rll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn\Resources\1033; Flags: uninsneveruninstall; Components: sqldmo
; Italian Resources
Source: Files\dmo\Resources\1044\Sqldmo.rll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn\Resources\1044; Flags: uninsneveruninstall; Components: sqldmo
Source: Files\dmo\Resources\1044\sqlsvc.rll; DestDir: {pf}\Microsoft SQL Server\80\Tools\Binn\Resources\1044; Flags: uninsneveruninstall; Components: sqldmo


61 - Libray for NetAPI functions.

Article created: 17/09/03 0:31:35
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=61

Library for NetAPI functions, include a function to convert ansi string to UNICODE strings.

[Code]

Const

  NERR_Success = 0;
  NERR_BASE    =   2100;

  NERR_BadUsername = (NERR_BASE+102);
  NERR_InvalidComputer  = (NERR_BASE+251);
  NERR_NotPrimary  = (NERR_BASE+126);
  NERR_GroupExists  = (NERR_BASE+123);
  NERR_UserExists  = (NERR_BASE+124);
  NERR_PasswordTooShort  = (NERR_BASE+145);

  USER_PRIV_GUEST = 0;
  USER_PRIV_USER = 1;
  USER_PRIV_ADMIN = 2;

  UF_SCRIPT = $0001;
  UF_ACCOUNTDISABLE = $0002;
  UF_HOMEDIR_REQUIRED = $0008;
  UF_LOCKOUT = $0010;
  UF_PASSWD_NOTREQD = $0020;
  UF_PASSWD_CANT_CHANGE = $0040;

  UF_TEMP_DUPLICATE_ACCOUNT = $0100;
  UF_NORMAL_ACCOUNT = $0200;
  UF_INTERDOMAIN_TRUST_ACCOUNT = $0800;
  UF_WORKSTATION_TRUST_ACCOUNT = $1000;
  UF_SERVER_TRUST_ACCOUNT = $2000;

  UF_DONT_EXPIRE_PASSWD = $10000;

  DOMAIN_GROUP_RID_ADMINS = $00000200;
  DOMAIN_GROUP_RID_USERS = $00000201;
  DOMAIN_GROUP_RID_GUESTS = $00000202;
  DOMAIN_GROUP_RID_COMPUTERS = $00000203;
  DOMAIN_GROUP_RID_CONTROLLERS = $00000204;
  DOMAIN_GROUP_RID_CERT_ADMINS = $00000205;
  DOMAIN_GROUP_RID_SCHEMA_ADMINS = 00000206;

type
    NET_API_STATUS = LongInt;

type
  TUserInfo1 = record
   usri1_name : PChar;
   usri1_password : PChar;
   usri1_password_age: LongInt;
   usri1_priv: LongInt;
   usri1_home_dir: PChar;
   usri1_comment: PChar;
   usri1_flags: LongInt;
   usri1_script_path:PChar;
  end;

type
  TUserInfo3 = record
    usri3_name : PChar;
    usri3_password: PChar;
    usri3_password_age: LongInt;
    usri3_priv: LongInt;
    usri3_home_dir: PChar;
    usri3_comment: PChar;
    usri3_flags: LongInt;
    usri3_script_path: PChar;
    usri3_auth_flags: LongInt;
    usri3_full_name: PChar;
    usri3_usr_comment: PChar;
    usri3_parms: PChar;
    usri3_workstations: PChar;
    usri3_last_logon: LongInt;
    usri3_last_logoff: LongInt;
    usri3_acct_expires: LongInt;
    usri3_max_storage: LongInt;
    usri3_units_per_week: LongInt;
    usri3_logon_hours: LongInt;
    usri3_bad_pw_count: LongInt;
    usri3_num_logons: LongInt;
    usri3_logon_server: PChar;
    usri3_country_code: LongInt;
    usri3_code_page: LongInt;
    usri3_user_id: LongInt;
    usri3_primary_group_id: LongInt;
    usri3_profile: PChar;
    usri3_home_dir_drive: PChar;
    usri3_password_expired: LongInt;
end;

function NetUserAdd3(
  servername: PChar;
  level: LongInt;
  var info3: TUserInfo3;
  var param_err: LongInt ): NET_API_STATUS;
external 'NetUserAdd@NETAPI32.DLL stdcall';

function NetUserAdd1(
  servername: PChar;
  level: LongInt;
  var info1: TUserInfo1;
  var param_err: LongInt ): NET_API_STATUS;
external 'NetUserAdd@NETAPI32.DLL stdcall';

//
// Ansi2Unicode
//
function Ansi2Unicode( s: String ) : String;
var
    z : Char; i : Integer;
    uni: String;
begin

  z := Chr(0);

  for i := 1 to Length(s) do
      uni := uni + StrGet(s, i ) + z;

  Result := uni;
end;


62 - How to add a local user

Article created: 17/09/03 0:15:48
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=62

This sample shows how to create a local user on Windows 2000.

To compile this sample you need NetAPI function library.

;
; ISX 3.0.6.2

[Setup]
AppName=NetUserAdd
AppVerName=NetUserAdd
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\NetUserAdd
DisableStartupPrompt=true
CreateAppDir=false
OutputDir=.
OutputBaseFilename=NetUserAdd
Compression=bzip
AllowUNCPath=false
; only on NT and above
MinVersion=0,4.00.1381
;Admin prilileges necessary
PrivilegesRequired=admin

[_ISTool]
EnableISX=true

[Code]

#include "netapilib.iss"

var

  // Custom pages controls
  butCreate: TButton;
  edUserName, edComment, edPassword: TEdit;
  lbUserName, lbComment, lbPassword: TLabel;

  // variables
  username, comment, password: String;

//
// Create User
//
function CreateUser( uname, com, pwd : String ) : Boolean;
var u3: TUserInfo3;
    res: NET_API_STATUS;
    perr: LongInt;
begin
  u3.usri3_name := PChar(Ansi2Unicode(uname));
  u3.usri3_priv := USER_PRIV_USER;
  u3.usri3_comment := PChar(Ansi2Unicode(com));
  u3.usri3_password := PChar(Ansi2Unicode(pwd));
  u3.usri3_full_name := PChar(Ansi2Unicode(uname + ' (full name)'));
  u3.usri3_flags := UF_NORMAL_ACCOUNT ;
  u3.usri3_primary_group_id := DOMAIN_GROUP_RID_USERS;

  res := NetUserAdd3(
                CastIntegerToString(0), // server name null is this pc
                3, // structure USER_INFO_3
                u3,
                perr );

  if res = NERR_Success then begin
    MsgBox ('User created!' ,0,0);
    Result := true;
  end
  else begin
    MsgBox ('Create user failed: ' + uname + #13#10#13#10
      + IntToStr(res) + ' ' + SysErrorMessage( res ),
      mbError,MB_OK);
    Result := false;
  end;

end;

function InitializeSetup() : boolean;
begin

  username := 'NewUser';
  comment := 'NewUser comment';
  password := '';

  Result := True;
end;

procedure butCreateOnClick(Sender: TObject);
begin
    WizardForm.Enabled := false;

    username := edUserName.Text;
    comment := edComment.Text;
    password := edPassword.Text;

    CreateUser( username, comment, password );

    WizardForm.Enabled := true;
end;

function UserInfosPage( BackClicked: Boolean): Boolean;
var

  Next: Boolean;
begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('Create e new user');
    ScriptDlgPageSetSubCaption1('Info about new user');

    lbUserName := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbUserName.Top := 0;
    lbUserName.Caption := 'User Name:';
    lbUserName.AutoSize := True;
    lbUserName.Parent := WizardForm.ScriptDlgPanel;

    edUserName := TEdit.Create(WizardForm.ScriptDlgPanel);
    edUserName.Top := lbUserName.Top + lbUserName.Height + 8;
    edUserName.Text := username;
    edUserName.Width := WizardForm.ScriptDlgPanel.Width;
    edUserName.Parent := WizardForm.ScriptDlgPanel;
    edUserName.AutoSelect := true;

    lbComment := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbComment.Top := edUserName.Top + edUserName.Height + 8;
    lbComment.Caption := 'Comment:';
    lbComment.AutoSize := True;
    lbComment.Parent := WizardForm.ScriptDlgPanel;

    edComment := TEdit.Create(WizardForm.ScriptDlgPanel);
    edComment.Top := lbComment.Top + lbComment.Height + 8;
    edComment.Text := comment;
    edComment.Width := WizardForm.ScriptDlgPanel.Width;
    edComment.Parent := WizardForm.ScriptDlgPanel;

    lbPassword := TLabel.Create(WizardForm.ScriptDlgPanel);
    lbPassword.Top := edComment.Top + edComment.Height + 8;
    lbPassword.Caption := 'Password:';
    lbPassword.AutoSize := True;
    lbPassword.Parent := WizardForm.ScriptDlgPanel;

    edPassword := TEdit.Create(WizardForm.ScriptDlgPanel);
    edPassword.Top := lbPassword.Top + lbPassword.Height + 8;
    edPassword.Text := password;
    edPassword.Width := WizardForm.ScriptDlgPanel.Width;
    edPassword.Parent := WizardForm.ScriptDlgPanel;
    edPassword.PasswordChar := '*';

    butCreate := TButton.Create(WizardForm.ScriptDlgPanel);
    butCreate.Top := WizardForm.ScriptDlgPanel.Height - butCreate.Height;
    butCreate.Caption := 'Create User';
    butCreate.Width := WizardForm.ScriptDlgPanel.Width / 3 ;
    butCreate.Parent := WizardForm.ScriptDlgPanel;
    butCreate.OnClick := @butCreateOnClick;

    Next := ScriptDlgPageProcessCustom();

    username := edUserName.Text;
    comment := edComment.Text;
    password := edPassword.Text;

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    Result := UserInfosPage( BackClicked )
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


63 - Sample script to help understand when and which event is triggered

Article created: 13/10/03 23:30:26
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=63

Author: Wolf

This is ISS file which I wrote to help me understand when which functions/procedures/events are triggered.
The procdure/function calls and parameters are written into a file 'C:\Log_IS_Events.txt'.

It is possible download ISS file and text file needed to compile script.
Thought that it might also be usefull to other people.

[Setup]
OutputBaseFilename=TestEventsSetup
AppName=TestEvents
AppVerName=TestEvents V1
DefaultDirName={pf}\TestEvents
AppID=TestEevents
ShowTasksTreeLines=false
RestartIfNeededByRun=false
UserInfoPage=true
DefaultGroupName=TestEvents
InfoAfterFile=.\InfoAfterFile.txt
InfoBeforeFile=.\InfoBeforeFile.txt
LicenseFile=.\LicenseFile.txt
OutputDir=.\

[Files]
Source: Test Events.iss; DestDir: {app}

[Icons]
Name: {group}\Uninstall Test Events; Filename: {uninstallexe}
Name: {group}\Test Events; Filename: {app}\Test Events.iss; WorkingDir: {app}; Comment: Test Events; Flags: createonlyiffileexists

[_ISTool]
EnableISX=true
UseAbsolutePaths=false

[Code]
var
    mcLogFile: String;

function InitializeSetup(): Boolean;
// return False to abort Setup
begin
    mcLogFile := 'C:\Log_IS_Events.txt';
    DeleteFile( mcLogFile);
    SaveStringToFile(mcLogFile, 'In function InitializeSetup' + Chr(13), True);
    Result := True;
end;

procedure InitializeWizard();
// Use this event function to make changes to the wizard or wizard pages at startup.
// You can't use the InitializeSetup event function for this since at the time it is triggered,
// the wizard form does not yet exist.
begin
    SaveStringToFile(mcLogFile, 'In procedure InitializeWizard' + Chr(13), True);
end;

procedure DeInitializeSetup();
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure DeInitializeSetup' + Chr(13), True);
end;

procedure CurStepChanged(CurStep: Integer);
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure CurStepChanged ' + IntToStr(CurStep), True);
    case CurStep of
        csStart :
          SaveStringToFile(mcLogFile, ' : csStart' + Chr(13), True);
        csWizard :
          SaveStringToFile(mcLogFile, ' : csWizard' + Chr(13), True);
        csCopy :
          SaveStringToFile(mcLogFile, ' : csCopy' + Chr(13), True);
        csFinished :
          SaveStringToFile(mcLogFile, ' : csFinished' + Chr(13), True);
        csTerminate :
          SaveStringToFile(mcLogFile, ' : csTerminate' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function NextButtonClick(CurPage: Integer): Boolean;
// return False to surpress the click on the Next button
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function NextButtonClick ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

function BackButtonClick(CurPage: Integer): Boolean;
// return False to surpress the click on the Back button
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function BackButtonClick ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

function SkipCurPage(CurPage: Integer): Boolean;
// return True to skip the CurPage page
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function SkipCurPage ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := False;
end;

procedure CurPageChanged(CurPage: Integer);
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure CurPageChanged ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function CheckPassword(Password: String): Boolean;
// If Setup finds the CheckPassword event function in the Pascal script, it automatically
// displays the Password page and calls CheckPassword to check passwords.
// Return True to accept the password and False to reject it.
// When using a password, it's important to keep in mind that since no encryption is used and
// the source code to Inno Setup is freely available, it would not be too difficult for an experienced
// individual to remove the password protection from an installation.
// Use this only as a 'deterrent' to keep unauthorized people out of your installations.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function CheckPassword ' + Password + Chr(13), True);
    Result := True;
end;

function NeedRestart(): Boolean;
// return True to instruct Setup to prompt the user to restart the system at the end of a successful installation.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function NeedRestart' + Chr(13), True);
    Result := False;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
// If Setup finds the UpdateReadyMemo event function in the Pascal script, it is called automatically
//  when the Ready to Install wizard page becomes the active page.
// It should return the text to be displayed in the settings memo on the Ready to Install wizard page
// as a single string with lines separated by the NewLine parameter.
// Parameter Space contains a string with spaces. Setup uses this string to indent settings.
// The other parameters contain the (possibly empty) strings that Setup would have used as the setting sections.
// The MemoDirInfo parameter for example contains the string for the Selected Directory section.
// return False to abort Setup
var
    cTemp: String;
begin
    SaveStringToFile(mcLogFile, 'In function UpdateReadyMemo' + Chr(13), True);
    cTemp := MemoUserInfoInfo + NewLine;
    cTemp := cTemp + MemoDirInfo + NewLine;
    cTemp := cTemp + MemoTypeInfo + NewLine;
    cTemp := cTemp + MemoComponentsInfo + NewLine;
    cTemp := cTemp + MemoGroupInfo + NewLine;
    cTemp := cTemp + MemoTasksInfo + NewLine;
    Result := cTemp;
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
// To store user settings entered on custom wizard pages, place a RegisterPreviousData event function
// in the Pascal script and call SetPreviousData(PreviousDataKey, ...) inside it, once per setting.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure PreviousDataKey' + Chr(13), True);
end;

function CheckSerial(Serial: String): Boolean;
// If Setup finds the CheckSerial event function in the Pascal script, a serial number field
// with automatically appear on the User Info wizard page (which must be enabled using
// UserInfoPage=yes in your [Setup] section!).
// Return True to accept the serial number and False to reject it.
// When using serial numbers, it's important to keep in mind that since no encryption is used and
// the source code to Inno Setup is freely available, it would not be too difficult for an
// experienced individual to remove the serial number protection from an installation.
// Use this only as a convienience to the end user and double check the entered serial number
// (stored in the {userinfoserial} constant) in your application.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function CheckSerial ' + Serial + Chr(13), True);
    Result := True;
end;

// Here's the list of constants used by these functions:

// CurStep values
// csStart, csWizard, csCopy, csFinished, csTerminate

// CurPage values
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir,
// wpSelectComponents, wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing,
// wpInstalling, wpInfoAfter, wpFinished

// None of these functions are required to be present in a Pascal script.

// SkipCurPage isn't called if Setup already determined that the page should be skipped or
// for the Welcome page or for the Installing page.


64 - Library to format date and time

Article created: 27/01/04 0:41:29
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=64

Library to format date and time.
LangLib.iss

;
; Language Libray
;

[Code]

type LCID = LongInt;
type LANGID = WORD;

//
//  Language IDs.
//
//  The following two combinations of primary language ID and
//  sublanguage ID have special semantics:
//
//    Primary Language ID   Sublanguage ID      Result
//    -------------------   ---------------     ------------------------
//    LANG_NEUTRAL          SUBLANG_NEUTRAL     Language neutral
//    LANG_NEUTRAL          SUBLANG_DEFAULT     User default language
//    LANG_NEUTRAL          SUBLANG_SYS_DEFAULT System default language
//

//
//  Primary language IDs.
//

Const LANG_NEUTRAL                     = $00;

Const LANG_AFRIKAANS                   = $36;
Const LANG_ALBANIAN                    = $1c;
Const LANG_ARABIC                      = $01;
Const LANG_ARMENIAN                    = $2b;
Const LANG_ASSAMESE                    = $4d;
Const LANG_AZERI                       = $2c;
Const LANG_BASQUE                      = $2d;
Const LANG_BELARUSIAN                  = $23;
Const LANG_BENGALI                     = $45;
Const LANG_BULGARIAN                   = $02;
Const LANG_CATALAN                     = $03;
Const LANG_CHINESE                     = $04;
Const LANG_CROATIAN                    = $1a;
Const LANG_CZECH                       = $05;
Const LANG_DANISH                      = $06;
Const LANG_DUTCH                       = $13;
Const LANG_ENGLISH                     = $09;
Const LANG_ESTONIAN                    = $25;
Const LANG_FAEROESE                    = $38;
Const LANG_FARSI                       = $29;
Const LANG_FINNISH                     = $0b;
Const LANG_FRENCH                      = $0c;
Const LANG_GEORGIAN                    = $37;
Const LANG_GERMAN                      = $07;
Const LANG_GREEK                       = $08;
Const LANG_GUJARATI                    = $47;
Const LANG_HEBREW                      = $0d;
Const LANG_HINDI                       = $39;
Const LANG_HUNGARIAN                   = $0e;
Const LANG_ICELANDIC                   = $0f;
Const LANG_INDONESIAN                  = $21;
Const LANG_ITALIAN                     = $10;
Const LANG_JAPANESE                    = $11;
Const LANG_KANNADA                     = $4b;
Const LANG_KASHMIRI                    = $60;
Const LANG_KAZAK                       = $3f;
Const LANG_KONKANI                     = $57;
Const LANG_KOREAN                      = $12;
Const LANG_LATVIAN                     = $26;
Const LANG_LITHUANIAN                  = $27;
Const LANG_MACEDONIAN                  = $2f;
Const LANG_MALAY                       = $3e;
Const LANG_MALAYALAM                   = $4c;
Const LANG_MANIPURI                    = $58;
Const LANG_MARATHI                     = $4e;
Const LANG_NEPALI                      = $61;
Const LANG_NORWEGIAN                   = $14;
Const LANG_ORIYA                       = $48;
Const LANG_POLISH                      = $15;
Const LANG_PORTUGUESE                  = $16;
Const LANG_PUNJABI                     = $46;
Const LANG_ROMANIAN                    = $18;
Const LANG_RUSSIAN                     = $19;
Const LANG_SANSKRIT                    = $4f;
Const LANG_SERBIAN                     = $1a;
Const LANG_SINDHI                      = $59;
Const LANG_SLOVAK                      = $1b;
Const LANG_SLOVENIAN                   = $24;
Const LANG_SPANISH                     = $0a;
Const LANG_SWAHILI                     = $41;
Const LANG_SWEDISH                     = $1d;
Const LANG_TAMIL                       = $49;
Const LANG_TATAR                       = $44;
Const LANG_TELUGU                      = $4a;
Const LANG_THAI                        = $1e;
Const LANG_TURKISH                     = $1f;
Const LANG_UKRAINIAN                   = $22;
Const LANG_URDU                        = $20;
Const LANG_UZBEK                       = $43;
Const LANG_VIETNAMESE                  = $2a;

//
//  Sublanguage IDs.
//
//  The name immediately following SUBLANG_ dictates which primary
//  language ID that sublanguage ID can be combined with to form a
//  valid language ID.
//

Const SUBLANG_NEUTRAL                  = $00;    // language neutral
Const SUBLANG_DEFAULT                  = $01;    // user default
Const SUBLANG_SYS_DEFAULT              = $02;    // system default

Const SUBLANG_ARABIC_SAUDI_ARABIA      = $01;    // Arabic (Saudi Arabia)
Const SUBLANG_ARABIC_IRAQ              = $02;    // Arabic (Iraq)
Const SUBLANG_ARABIC_EGYPT             = $03;    // Arabic (Egypt)
Const SUBLANG_ARABIC_LIBYA             = $04;    // Arabic (Libya)
Const SUBLANG_ARABIC_ALGERIA           = $05;    // Arabic (Algeria)
Const SUBLANG_ARABIC_MOROCCO           = $06;    // Arabic (Morocco)
Const SUBLANG_ARABIC_TUNISIA           = $07;    // Arabic (Tunisia)
Const SUBLANG_ARABIC_OMAN              = $08;    // Arabic (Oman)
Const SUBLANG_ARABIC_YEMEN             = $09;    // Arabic (Yemen)
Const SUBLANG_ARABIC_SYRIA             = $0a;    // Arabic (Syria)
Const SUBLANG_ARABIC_JORDAN            = $0b;    // Arabic (Jordan)
Const SUBLANG_ARABIC_LEBANON           = $0c;    // Arabic (Lebanon)
Const SUBLANG_ARABIC_KUWAIT            = $0d;    // Arabic (Kuwait)
Const SUBLANG_ARABIC_UAE               = $0e;    // Arabic (U.A.E)
Const SUBLANG_ARABIC_BAHRAIN           = $0f;    // Arabic (Bahrain)
Const SUBLANG_ARABIC_QATAR             = $10;    // Arabic (Qatar)
Const SUBLANG_AZERI_LATIN              = $01;    // Azeri (Latin)
Const SUBLANG_AZERI_CYRILLIC           = $02;    // Azeri (Cyrillic)
Const SUBLANG_CHINESE_TRADITIONAL      = $01;    // Chinese (Taiwan Region)
Const SUBLANG_CHINESE_SIMPLIFIED       = $02;    // Chinese (PR China)
Const SUBLANG_CHINESE_HONGKONG         = $03;    // Chinese (Hong Kong)
Const SUBLANG_CHINESE_SINGAPORE        = $04;    // Chinese (Singapore)
Const SUBLANG_CHINESE_MACAU            = $05;    // Chinese (Macau)
Const SUBLANG_DUTCH                    = $01;    // Dutch
Const SUBLANG_DUTCH_BELGIAN            = $02;    // Dutch (Belgian)
Const SUBLANG_ENGLISH_US               = $01;    // English (USA)
Const SUBLANG_ENGLISH_UK               = $02;    // English (UK)
Const SUBLANG_ENGLISH_AUS              = $03;    // English (Australian)
Const SUBLANG_ENGLISH_CAN              = $04;    // English (Canadian)
Const SUBLANG_ENGLISH_NZ               = $05;    // English (New Zealand)
Const SUBLANG_ENGLISH_EIRE             = $06;    // English (Irish)
Const SUBLANG_ENGLISH_SOUTH_AFRICA     = $07;    // English (South Africa)
Const SUBLANG_ENGLISH_JAMAICA          = $08;    // English (Jamaica)
Const SUBLANG_ENGLISH_CARIBBEAN        = $09;    // English (Caribbean)
Const SUBLANG_ENGLISH_BELIZE           = $0a;    // English (Belize)
Const SUBLANG_ENGLISH_TRINIDAD         = $0b;    // English (Trinidad)
Const SUBLANG_ENGLISH_ZIMBABWE         = $0c;    // English (Zimbabwe)
Const SUBLANG_ENGLISH_PHILIPPINES      = $0d;    // English (Philippines)
Const SUBLANG_FRENCH                   = $01;    // French
Const SUBLANG_FRENCH_BELGIAN           = $02;    // French (Belgian)
Const SUBLANG_FRENCH_CANADIAN          = $03;    // French (Canadian)
Const SUBLANG_FRENCH_SWISS             = $04;    // French (Swiss)
Const SUBLANG_FRENCH_LUXEMBOURG        = $05;    // French (Luxembourg)
Const SUBLANG_FRENCH_MONACO            = $06;    // French (Monaco)
Const SUBLANG_GERMAN                   = $01;    // German
Const SUBLANG_GERMAN_SWISS             = $02;    // German (Swiss)
Const SUBLANG_GERMAN_AUSTRIAN          = $03;    // German (Austrian)
Const SUBLANG_GERMAN_LUXEMBOURG        = $04;    // German (Luxembourg)
Const SUBLANG_GERMAN_LIECHTENSTEIN     = $05;    // German (Liechtenstein)
Const SUBLANG_ITALIAN                  = $01;    // Italian
Const SUBLANG_ITALIAN_SWISS            = $02;    // Italian (Swiss)
Const SUBLANG_KASHMIRI_INDIA           = $02;    // Kashmiri (India)
Const SUBLANG_KOREAN                   = $01;    // Korean (Extended Wansung)
Const SUBLANG_LITHUANIAN               = $01;    // Lithuanian
Const SUBLANG_LITHUANIAN_CLASSIC       = $02;    // Lithuanian (Classic)
Const SUBLANG_MALAY_MALAYSIA           = $01;    // Malay (Malaysia)
Const SUBLANG_MALAY_BRUNEI_DARUSSALAM  = $02;    // Malay (Brunei Darussalam)
Const SUBLANG_NEPALI_INDIA             = $02;    // Nepali (India)
Const SUBLANG_NORWEGIAN_BOKMAL         = $01;    // Norwegian (Bokmal)
Const SUBLANG_NORWEGIAN_NYNORSK        = $02;    // Norwegian (Nynorsk)
Const SUBLANG_PORTUGUESE               = $02;    // Portuguese
Const SUBLANG_PORTUGUESE_BRAZILIAN     = $01;    // Portuguese (Brazilian)
Const SUBLANG_SERBIAN_LATIN            = $02;    // Serbian (Latin)
Const SUBLANG_SERBIAN_CYRILLIC         = $03;    // Serbian (Cyrillic)
Const SUBLANG_SPANISH                  = $01;    // Spanish (Castilian)
Const SUBLANG_SPANISH_MEXICAN          = $02;    // Spanish (Mexican)
Const SUBLANG_SPANISH_MODERN           = $03;    // Spanish (Modern)
Const SUBLANG_SPANISH_GUATEMALA        = $04;    // Spanish (Guatemala)
Const SUBLANG_SPANISH_COSTA_RICA       = $05;    // Spanish (Costa Rica)
Const SUBLANG_SPANISH_PANAMA           = $06;    // Spanish (Panama)
Const SUBLANG_SPANISH_DOMINICAN_REPUBLIC = $07;  // Spanish (Dominican Republic)
Const SUBLANG_SPANISH_VENEZUELA        = $08;    // Spanish (Venezuela)
Const SUBLANG_SPANISH_COLOMBIA         = $09;    // Spanish (Colombia)
Const SUBLANG_SPANISH_PERU             = $0a;    // Spanish (Peru)

Const SUBLANG_SPANISH_ARGENTINA        = $0b;    // Spanish (Argentina)
Const SUBLANG_SPANISH_ECUADOR          = $0c;    // Spanish (Ecuador)
Const SUBLANG_SPANISH_CHILE            = $0d;    // Spanish (Chile)
Const SUBLANG_SPANISH_URUGUAY          = $0e;    // Spanish (Uruguay)
Const SUBLANG_SPANISH_PARAGUAY         = $0f;    // Spanish (Paraguay)
Const SUBLANG_SPANISH_BOLIVIA          = $10;    // Spanish (Bolivia)
Const SUBLANG_SPANISH_EL_SALVADOR      = $11;    // Spanish (El Salvador)
Const SUBLANG_SPANISH_HONDURAS         = $12;    // Spanish (Honduras)
Const SUBLANG_SPANISH_NICARAGUA        = $13;    // Spanish (Nicaragua)
Const SUBLANG_SPANISH_PUERTO_RICO      = $14;    // Spanish (Puerto Rico)
Const SUBLANG_SWEDISH                  = $01;    // Swedish
Const SUBLANG_SWEDISH_FINLAND          = $02;    // Swedish (Finland)
Const SUBLANG_URDU_PAKISTAN            = $01;    // Urdu (Pakistan)
Const SUBLANG_URDU_INDIA               = $02;    // Urdu (India)
Const SUBLANG_UZBEK_LATIN              = $01;    // Uzbek (Latin)
Const SUBLANG_UZBEK_CYRILLIC           = $02;    // Uzbek (Cyrillic)

//
//  Sorting IDs.
//

Const SORT_DEFAULT                     = $0;     // sorting default

Const SORT_JAPANESE_XJIS               = $0;     // Japanese XJIS order
Const SORT_JAPANESE_UNICODE            = $1;     // Japanese Unicode order

Const SORT_CHINESE_BIG5                = $0;     // Chinese BIG5 order
Const SORT_CHINESE_PRCP                = $0;     // PRC Chinese Phonetic order
Const SORT_CHINESE_UNICODE             = $1;     // Chinese Unicode order
Const SORT_CHINESE_PRC                 = $2;     // PRC Chinese Stroke Count order
Const SORT_CHINESE_BOPOMOFO            = $3;     // Traditional Chinese Bopomofo order

Const SORT_KOREAN_KSC                  = $0;     // Korean KSC order
Const SORT_KOREAN_UNICODE              = $1;     // Korean Unicode order

Const SORT_GERMAN_PHONE_BOOK           = $1;     // German Phone Book order

Const SORT_HUNGARIAN_DEFAULT           = $0;     // Hungarian Default order
Const SORT_HUNGARIAN_TECHNICAL         = $1;     // Hungarian Technical order

Const SORT_GEORGIAN_TRADITIONAL        = $0;     // Georgian Traditional order
Const SORT_GEORGIAN_MODERN             = $1;     // Georgian Modern order

//
//  Time Flags for GetTimeFormat.
//
Const TIME_NOMINUTESORSECONDS   = $1;  // do not use minutes or seconds
Const TIME_NOSECONDS            = $2;  // do not use seconds
Const TIME_NOTIMEMARKER         = $4;  // do not use time marker
Const TIME_FORCE24HOURFORMAT    = $8;  // always use 24 hour format

//
//  Date Flags for GetDateFormat.
//
Const LOCALE_NOUSEROVERRIDE     = $80000000;
Const DATE_SHORTDATE            = $1;  // use short date picture
Const DATE_LONGDATE             = $2;  // use long date picture
Const DATE_USE_ALT_CALENDAR     = $4;  // use alternate calendar (if any)
Const DATE_YEARMONTH            = $8;  // use year month picture
Const DATE_LTRREADING           = $10;  // add marks for left to right reading order layout
Const DATE_RTLREADING           = $20;  // add marks for right to left reading order layout

function MakeLangId(usPrimaryLanguage,usSubLanguage : byte) : LANGID;
begin
 Result := (usSubLanguage shl 10) or usPrimaryLanguage;
end;

function MakeLCID(wLanguageID,wSortID : word) : LCID;
begin
 Result := (wSortId shl 16) or wLanguageID;
end;

// LCID GetUserDefaultLCID(VOID)
function GetUserDefaultLCID() : LCID; external
 'GetUserDefaultLCID@kernel32.dll stdcall';

// LCID GetSystemDefaultLCID(VOID)
function GetSystemDefaultLCID() : LCID; external
 'GetSystemDefaultLCID@kernel32.dll stdcall';

//
//  Default System and User IDs for language and locale.
//

var
 LANG_SYSTEM_DEFAULT, LANG_USER_DEFAULT : LANGID;
 LOCALE_USER_DEFAULT, LOCALE_NEUTRAL : LCID;

Procedure InitLang();
begin

 LANG_SYSTEM_DEFAULT := MakeLangId(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT);
 LANG_USER_DEFAULT := MakeLangId(LANG_NEUTRAL, SUBLANG_DEFAULT);
 LOCALE_USER_DEFAULT   := MakeLCID(LANG_USER_DEFAULT, SORT_DEFAULT);
 LOCALE_NEUTRAL        := MakeLCID(MakeLangId(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT);

end;


65 - How to format date and time

Article created: 27/01/04 0:45:41
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=65

Sample on how to detect language ID, and format date and time with that LangID or with a custom format.

To compile this sample you need Language library.

[Setup]
AppName=DateFormat
AppVerName=DateFormat
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\DateFormat
DisableStartupPrompt=true

[Code]

#include "LangLib.iss"

type
  TSystemTime = record
    wYear : Word;
    wMonth : Word;
    wDayOfWeek : Word;
    wDay : Word;
    wHour : Word;
    wMinute : Word;
    wSecond : Word;
    wMilliseconds : Word;
  end;

procedure GetLocalTime(var lpSystemTime: TSystemTime); external
'GetLocalTime@kernel32.dll';

function GetDateFormat(Locale: Integer;dwFlags: LongInt;
   var lpDate: TSystemTime; lpFormat: PChar; lpDateStr: String;
   cchDate: Integer): Integer; external
   'GetDateFormatA@kernel32.dll';

function GetTimeFormat(Locale: Integer;dwFlags: LongInt;
   var lpTime: TSystemTime;lpFormat: PChar;lpTimeStr: String;
   cchDate: Integer): Integer; external
   'GetTimeFormatA@kernel32.dll';

function GetlastError(): Integer; external
  'GetLastError@kernel32.dll';

var
   lt : TSystemTime;

function InitializeSetup: Boolean;
begin

   // Initialize Lanuage variables
   InitLang;

   GetLocalTime(lt);

   Result := true;
end;

procedure AddDateTime( list: TlistBox; desc: String; lc: LCID );
var
  datestring, timestring : String;
begin

    list.Items.Add( desc + ' = ' + IntToStr( lc ) );

  datestring := StringOfChar(' ',64);
  GetDateFormat(lc, DATE_LONGDATE,lt, '' ,datestring,63);
  datestring := CastIntegerToString( CastStringToInteger(datestring) );
    list.Items.Add( '     Date = ' + datestring );

  timestring := StringOfChar(' ',64);
  GetTimeFormat(lc, 0, lt , '', timestring,63);
  timestring := CastIntegerToString( CastStringToInteger(timestring) );
    list.Items.Add( '     Time = ' + timestring );

end;

function DateTimeFormats( BackClicked: Boolean): Boolean;
var
  MyList : TListBox;
  Next: Boolean;
  mylcid :LCID;
  datestring, timestring, format: string;

begin

    // First open the custom wizard page
    ScriptDlgPageOpen();

    // Set some captions
    ScriptDlgPageSetCaption('Date Time Formats');
    ScriptDlgPageSetSubCaption1('List of date and time formats');

    MyList := TListBox.Create(WizardForm.ScriptDlgPanel);
    MyList.Top := 8;
    MyList.Width := WizardForm.ScriptDlgPanel.Width;
    MyList.Parent := WizardForm.ScriptDlgPanel;
    MyList.Height := WizardForm.ScriptDlgPanel.Height - 24;

    AddDateTime( MyList, 'User Default LCID ', GetUserDefaultLCID );
    AddDateTime( MyList, 'Systemt Default LCID ', GetSystemDefaultLCID );

    mylcid := MakeLCID(MakeLangId(LANG_ITALIAN, SUBLANG_ITALIAN), SORT_DEFAULT);
    AddDateTime( MyList, 'Italian LCID ', mylcid );

    mylcid := MakeLCID(MakeLangId(LANG_SPANISH, SUBLANG_SPANISH_PUERTO_RICO), SORT_DEFAULT);
    AddDateTime( MyList, 'Spanish (Puerto Rico) LCID ', mylcid );

    mylcid := MakeLCID(MakeLangId(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), SORT_DEFAULT);
     AddDateTime( MyList, 'English (Irish) LCID ', mylcid );

    datestring := StringOfChar(' ',64);
    format := 'dd dddd MMMM yyyy';
    GetDateFormat(GetUserDefaultLCID,0,lt,format,datestring,63);
    datestring := CastIntegerToString( CastStringToInteger(datestring) );
    MyList.Items.Add( 'Custom format  <' + format + '> = ' + datestring );

    timestring := StringOfChar(' ',64);
    format := 'hh''--''mm''--''ss';
    GetTimeFormat(GetUserDefaultLCID,0,lt,format,timestring,63);
    timestring := CastIntegerToString( CastStringToInteger(timestring) );
    MyList.Items.Add( 'Custom format  <' + format + '> = ' + timestring );

    Next := ScriptDlgPageProcessCustom();

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    Result := DateTimeFormats( BackClicked )
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


66 - How to translate custom messages and labels on a multilingual setup

Article created: 14/02/04 0:38:13
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=66

Warning: latest Inno Setup implements {cm:} (custom messages) use this new functionality.

A multilingual setup needs messages and labels translated in many places, not only standard messages on wizard.
These items have to be translated:

  • Tasks descriptions
  • Group Tasks descriptions
  • Components descriptions
  • Custom control captions
  • Custom messages showed by code
It's also very useful to have all these translation on one fle for each language, and not inserted ih the setup script, so they can be easly managed and translated. So I think this is a good solution to achieve all these goals. It is necessary make a text file, INI format as Inno Setup stadard language file, one for every language.
Then it's necessary to incude these files in the setup but they havenot be installed, at the beginning of the setup, it's extracted the correct file based on the user selected language, then every time is necessary to show a translated label it is possible to call a function that read the correct message, for tasks and components description it is possible to use the {code:xxx} constant.
This is the English.lng file content:

[Buttons]
About=About...

[Messages]
AboutMsg=About message in English
TaskGroup=Group Description:
DescTask1=Task 1
DescTask2=Task 2
DescType1=Description Type 1
DescType2=Description Type 2
Comp1=Component 1
Comp2=Component 2

And this is the Italian.lng file content:

[Buttons]
About=Informazioni...

[Messages]
AboutMsg=Messaggio di informazioni in Italiano
TaskGroup=Operazioni Aggiuntive:
DescTask1=Operazione 1
DescTask2=Operazione 2
DescType1=Descrizione Tipo 1
DescType2=Descrizione Tipo 2
Comp1=Componente 1
Comp2=Componente 2

[Setup]
AppName=LangMsg
AppVerName=LangMsg
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\LangMsg
DisableStartupPrompt=true
Compression=bzip
ShowLanguageDialog=yes
LanguageDetectionMethod=none

[_ISTool]
Use7zip=false

[Languages]
; Defined languages
Name: en; MessagesFile: compiler:Default.isl
Name: it; MessagesFile: compiler:Italian.isl

[Files]
; files included but not installed, just for custom messages
Source: English.lng; DestDir: {tmp}; Flags: dontcopy
Source: Italian.lng; DestDir: {tmp}; Flags: dontcopy

[Types]
Name: Type1; Description: {code:GetLangMsg|DescType1}
Name: Type2; Description: {code:GetLangMsg|DescType2}; Flags: iscustom

[Components]
Name: Comp1; Description: {code:GetLangMsg|Comp1}; Types: Type1 Type2
Name: Comp2; Description: {code:GetLangMsg|Comp2}; Types: Type1

[Tasks]
Name: task1; Description: {code:GetLangMsg|DescTask1}; GroupDescription: {code:GetLangMsg|TaskGroup}
Name: task2; Description: {code:GetLangMsg|DescTask2}; GroupDescription: {code:GetLangMsg|TaskGroup}

[Code]

var
  // filename of custom message of selected language
  customfile: String;

function InitializeSetup(): Boolean;
var langsel : String;
begin

  // detect language choosed
  langsel := ExpandConstant('{language}');
  // detect which file extract
  if langsel = 'en' then customfile := 'English.lng';
  if langsel = 'it' then customfile := 'Italian.lng';
  // extract file
  ExtractTemporaryFile(customfile);
  // store custom file for future message retrieve
  customfile := ExpandConstant( '{tmp}' + customfile );
    Result := true;
end;

function GetCustomMsg( Section, Msg : String ) : String;
begin
  // retrieve text string for a section/key, if not found return ??? missing
  result := GetIniString( Section, Msg, '??? Missing '+Section+'-'+Msg, customfile );
end;

function GetLangMsg( Msg: String ) : String;
begin
  result := GetCustomMsg( 'Messages', Msg );
end;

procedure AboutButtonOnClick(Sender: TObject);
begin
  // show a custom message
  MsgBox( GetCustomMsg( 'Messages', 'AboutMsg' ) , mbInformation, mb_Ok);
end;

procedure InitializeWizard();
var
  AboutButton, CancelButton: TButton;
begin
  CancelButton := WizardForm.CancelButton;

  // create an about button
  AboutButton := TButton.Create(WizardForm);
  AboutButton.Left := WizardForm.ClientWidth - CancelButton.Left - CancelButton.Width;
  AboutButton.Top := CancelButton.Top;
  AboutButton.Width := CancelButton.Width;
  AboutButton.Height := CancelButton.Height;
  // button caption depends on selected language
  AboutButton.Caption := GetCustomMsg( 'Buttons','About' );
  AboutButton.OnClick := @AboutButtonOnClick;
  AboutButton.Parent := WizardForm;
end;


67 - How To: SplashScreen with Inno Setup 4.1.3

Article created: 14/02/04 17:00:30
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=67

Author: Benjamin

This script was originally posted on jrsoftware.innosetup.code newsgroup:

http://news.jrsoftware.org/news/innosetup.code/msg02028.html

Hi,

as many requested a splash in the past view weeks, i wrote some code that shows one. I decided to post the code here so anyone can use it - anyone who has constructive additions to the code please feel free to post them.

I am using a splash.bmp in the Inno Setup 4 directory.

[Setup]
AppName=My Splash
AppVerName=My Splash 1.5
AppPublisher=My Company, Inc.
AppPublisherURL=http://www.mycompany.com
AppSupportURL=http://www.mycompany.com
AppUpdatesURL=http://www.mycompany.com
DefaultDirName={pf}\My Splash

[Files]
Source: compiler:splash.bmp; DestDir: {tmp}; Flags: dontcopy

[Code]

procedure InitializeWizard();
var
  SplashImage: TBitmapImage;
  SplashForm: TForm;
  SplashFileName: String;
  I : Integer;
begin

  SplashFileName := ExpandConstant('{tmp}\splash.bmp');
  ExtractTemporaryFile(ExtractFileName(SplashFileName));

  SplashForm := TForm.create(nil);
  with SplashForm do
   begin
    BorderStyle := bsNone;
    Position := poScreenCenter;
    // Adjust the height and width of the SplashForm to the size of your splash image
    ClientWidth := 405;
    ClientHeight := 310;
   end;

  SplashImage := TBitmapImage.Create(SplashForm);
  with SplashImage do
    begin
    Bitmap.LoadFromFile(SplashFileName);
    Stretch := true;
    Align := alClient;
    Parent := SplashForm;
    end;

  with SplashForm do
   begin
     Show;
     // show for five seconds
     for I := 1 to 5 do
      begin
       Repaint;
       Sleep(1000);
      end;
     Close;
     Free;
   end;
end;


68 - How to extract script source from a compiled setup

Article created: 14/02/04 17:30:55
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=68

To extract a script from a compiled setup you can use Inno Setup Unpacker - http://innounp.sourceforge.net/.
However if you want to be sure to extract the script from a compiled setup you can do in this way.
  1. Include the script itself in the setup, it will not be installed
  2. Include in the setup any other script that is included at compile time
  3. Choose a key-password for a "restore" of the script
  4. Extract these files when user use the key as command parameter of the setup

[Setup]
AppName=LostScript
AppVerName=LostScript
DefaultDirName={pf}\LostScript
Uninstallable=false

[_ISTool]
Use7zip=false

[Files]
; store script in your setup, but do not install it
; add here also any other iss included
Source: LostScript.iss; Flags: dontcopy; DestDir: {tmp}

[Code]

const
// key that trigger extraction
  mykey = 'IHAVETOMAKEBACKUPS';
  scriptname = 'LostScript.iss';

function InitializeSetup(): Boolean;
var
  myfolder: String;
  gosetup : Boolean;
begin

  gosetup := true;

  // check if one command line parameter is the *secret key*
  if Pos( mykey, GetCmdTail ) > 0 then
  begin
    gosetup := false;

    // remember to the user (probably you) that it's very good make backups :)
    MsgBox( 'YOU HAVE TO MAKE BACKUPS OF YOUR SCRIPTS!!!', mbInformation, MB_OK );

    // ask where to copy the script
    if BrowseForFolder( 'Select where Script have to be placed', myfolder, true ) then
    begin
      // extract the script in the temp dir
      if ExtractTemporaryFile( scriptname) then
      begin
        // copy the file where user choosed
        if FileCopy( ExpandConstant('{tmp}'+scriptname), myfolder +''+scriptname , false ) then
        begin
          MsgBox( 'File extracted.', mbInformation, MB_OK );
        end
        else
          MsgBox( 'Failed to copy file.', mbError, MB_OK );
      end
      else
        MsgBox( 'Failed to extract file.', mbError, MB_OK );
    end;
  end;

    Result := gosetup;
end;


69 - Compile different builds in a snap

Article created: 08/05/04 11:20:20
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=69

Author: Bert Derijckere

When i first started using Inno Setup i had to compile on a 300Mhz machine (now compiling on 2.66Ghz), and i didn't want to wait for the compression while testing changes to the code.

So i came up with the following solution: (using ISPP)

I defined a couple of Flags for different build types and used appropriate #ifdef Flag ... #endif where needed

This gives me the options to compile different builds in a snap. When coding my [code] section i always compile as debug, so all the heavy files are left out. Saves me a lot of time. It also save the preprocessed script when doing a debug build so i can see what's happening with ISPP.

;Debug: no files, no compression, just test script, Send output to CATI-PP.iss
;DebugFiles: no compression but use files, to test setup, Send output to CATI-PP.iss
;Release: files with compression, to release smallest setup
;#define Debug
;#define DebugFiles
#define Release

;flags for Release/debug
#ifdef Debug
  #define SavePPOutput
#endif

#ifdef DebugFiles
  #define SavePPOutput
  #define Files
#endif

#ifdef Release
  #define Compression
  #define Files
#endif

[Setup]
AppName=MyProgram-differentBuilds
AppVerName=MyProgram-differentBuilds v1
DefaultDirName={app}
#ifdef Compression
Compression=lzma
InternalCompressLevel=max
SolidCompression=true
#else
Compression=none
InternalCompressLevel=none
SolidCompression=false
#endif
[Files]
#ifdef Files
Source: MyProgram.exe; DestDir: {app}
#endif //#ifdef Files

#ifdef SavePPOutput
  #expr SaveToFile(AddBackslash(SourcePath) + "differentBuild-PP.iss")
#endif


70 - Sample script to install Crystal Report 8 runtime files

Article created: 08/05/04 11:40:23
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=70

This script was built following directives in Crystal Report Developer guide, I adopted flag uninsneveruninstall because I prefer do not uninstall these files.

I created a folder creport and all the files in this folder will we installed in {sys}, there is also a subfolder crystal and all th files will be installed in {win}\crystal.

This script have to be modified if you need different database access or functions.

[Files]

;----------------------------
;  Crystal report 8
;----------------------------

;--------------
;  Begin Core files
;--------------
;Interface to the Crystal Report Engine
Source: creport\crpe32.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crpaig80.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystl32.ocx; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete regserver
Source: creport\implode.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\mfc42.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\mfcans32.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete

; Included in MDAC or Operating systems
; msvcrt.dll
; ?
Source: creport\msvcp60.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete

; olepro32.dll not included already in VB6 runtime

;--------------
; End Core files
;--------------

;--------------
; Begin Database Access
;--------------

;-- Access 7
Source: creport\p2bdao.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\p2ctdao.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\p2irdao.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
;-- Access 8 DAO
;-- ACT!
;Source: creport\crystal\p2bact.dll; DestDir: {win}\crystal;  Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2iract.dll; DestDir: {win}\crystal;  Flags: uninsneveruninstall uninsrestartdelete
;-- ACT 3 e 4
;Source: creport\crystal\p2bact3.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2iract3.dll; DestDir: {win}\crystal;  Flags: uninsneveruninstall uninsrestartdelete
; Active Data
Source: creport\p2smon.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
; BAAN
;Source: creport\crystal\p2sbaan.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\bn4c2nt2.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Btrieve
;Source: creport\crystal\p2bbtrv.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2ctbtrv.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Clipper dBase
Source: creport\crystal\p2bxbse.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; DB2
;Source: creport\crystal\p2sdb2.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2ldb2.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; BDE
Source: creport\crystal\p2bbde.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Exchanfe Outlook
;Source: creport\crystal\p2strack.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2smapi.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2sexsr.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2srepl.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2sacl.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2soutlk.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Informix
;Source: creport\crystal\p2lifmx.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2sifmx.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Local File System ?
;Source: creport\crystal\p2sfs.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Lotus NOTES
;Source: creport\crystal\p2snote.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; IIS Log
;Source: creport\crystal\p2smsiis.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; SQL Server 6.5
Source: creport\crystal\p2ssql.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\p2lsql.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; NT event Log
;Source: creport\crystal\p2sevt.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Oracle 7
;Source: creport\crystal\p2sora7.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2lora7.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; SAP
;Source: creport\crystal\p2srfc.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2srfcaq.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2srfcfa.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; Sybase
;Source: creport\crystal\p2ssyb10.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\p2lsyb10.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
; OLE DB
Source: creport\crystal\p2soledb.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete

;--------------
; End Database Access
;--------------

;--------------
; Begin ODBC Data Source
;--------------
Source: creport\p2sodbc.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete
;--------------
; End ODBC Data Source
;--------------

; BIND
Source: creport\p2bbnd.dll; DestDir: {sys}; Flags: uninsneveruninstall uninsrestartdelete

;--------------
; Exporting
;--------------
; Formats
Source: creport\crystal\u2fcr.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fdif.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fhtml.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fodbc.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
;Source: creport\crystal\u2fpdf.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2frdef.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2frec.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2frtf.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fsepv.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2ftext.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fwks.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fwordw.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2fxls.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete

; Destinations
Source: creport\crystal\u2dapp.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2ddisk.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2dmapi.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2dnotes.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2dpost.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete
Source: creport\crystal\u2dvim.dll; DestDir: {win}\crystal; Flags: uninsneveruninstall uninsrestartdelete

;--------------
; End Exporting
;--------------

;--------------
; USER Functions
;--------------
Source: creport\crystal\Crxlat32.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2lsamp1.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u25dts.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2ldts.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2lfinra.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2lexch.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2l2000.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u252000.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
Source: creport\crystal\u2lcom.dll; DestDir: {win}\Crystal; Flags: restartreplace uninsneveruninstall sharedfile
;--------------
; End USER Functions
;--------------

;----------------------------
; end Crystal report 8
;----------------------------


71 - How to check for .NET framework for Inno Setup 4

Article created: 08/05/04 13:18:42
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=71

Author: AG

On Form Initilize you can check whether .net framework is present or not.

Below Code first Checks for 1.0 and then for 1.1

Code for Inno Setup 5.X How to check for .NET framework for Inno Setup 5

[Setup]
AppName=NETFramewrok
AppVerName=NETFramewrok
DefaultDirName={pf}\NETFramewrok
DisableStartupPrompt=true

[Code]

function InitializeSetup(): Boolean;
var
    ErrorCode: Integer;
    NetFrameWorkInstalled : Boolean;
    Result1 : Boolean;
begin

           NetFrameWorkInstalled := RegKeyExists(HKLM,'SOFTWARE\Microsoft\.NETFramework\policy\v1.0');
           if NetFrameWorkInstalled =true then
           begin
              Result := true;
           end;
           if NetFrameWorkInstalled = false then
           begin
               NetFrameWorkInstalled := RegKeyExists(HKLM,'SOFTWARE\Microsoft\.NETFramework\policy\v1.1');
               if NetFrameWorkInstalled =true then
               begin
                  Result := true;
               end;
               if NetFrameWorkInstalled =false then
               begin
                         Result1 := MsgBox('This setup requires the .NET Framework. Please download and install the .NET Framework and run this setup again. Do you want to download the framwork now?',
                              mbConfirmation, MB_YESNO) = idYes;
                         if Result1 =false then
                         begin
                            Result:=false;
                         end
                         else
                         begin
                              Result:=false;
                                InstShellExec('http://download.microsoft.com/download/a/a/c/aac39226-8825-44ce-90e3-bf8203e74006/dotnetfx.exe','','',SW_SHOWNORMAL,ErrorCode);
                        end;
               end;
          end;
end;


72 - Function CopyFolder

Article created: 18/05/04 22:00:46
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=72

Author: Cesar Augusto Ronchese

Function CopyFolder.

[Code]
function CopyFolder(const SourceFolderPath: String; DestinyFolderPath: String): Boolean;
var
    strSrc: String;
    strDst: String;
begin

    //get first file
    strSrc := SourceFolderPath + '\*.*';
    strSrc := FindFirst(strSrc);

    while not (strSrc = '') do
    begin

        if (strSrc <> '.') and (strSrc <> '..') then
        begin

            // get the destiny and source paths
            strDst := DestinyFolderPath + '' + strSrc;
            strSrc := strKfxTempPath + '' + strSrc;

            if not FileCopy(strSrc, strDst, false) then
            begin
                //erro on copy
                MsgBox('Erro on copy ' + strSrc + ' to ' + strDst, mbError, MB_OK);
                Result := false;
                exit;
            end;
        end;

        //get next file
        strSrc := FindNext;

    end;

    //return OK
    Result := true;
end;


73 - How to detect Acrobat Reader version installed (2)

Article created: 18/05/04 22:10:12
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=73

Author: jon halle

How to detect Acrobat Reader version installed.

[Code]
function GetAcrobatReaderVersion(): Integer;
var
   sPath:  String;
   sVersion:  String;
   iPos: Integer;
   cChar: Char;
   sStart: String;
begin

   sVersion := '';
   sPath := '';
   RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe', '', sPath ) ;
   GetVersionNumbersString( sPath , sVersion );
   if  sVersion='' then
   begin
     RegQueryStringValue( HKLM, 'SOFTWARE\Classes\AcroExch.Document\AcrobatVersion', '', sVersion );
   end

   // now get whatever is before  dot
   iPos:=1;
   sStart:='';

   while iPos<Length(sVersion) do
   begin
       cChar:=StrGet(sVersion,iPos);
       if cChar='.' then
       begin
           iPos:=Length(sVersion);
       end
       else
       begin
           sStart := sStart+cChar;
           iPos := iPos +1;
       end
   end

   Result := StrToIntDef(sStart,0);

end;


74 - DirExists function to use in pre-processor directives

Article created: 18/05/04 22:13:28
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=74

Author: Jury Gerasimov

DirExists function to use in pre-processor directives.

#define DirExists(str Path) (Local[0] = FindFirst(Path, 0x10), Local[0] ? FindClose(Local[0]) : void, Local[0])


75 - How to open HTML page with MS IE

Article created: 18/05/04 22:15:05
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=75

Author: Jury Gerasimov

How to open HTML page with MS IE, even if it's not a default handler for *.html files.

[Run]
Filename: {code:IExplorePath|iexplore.exe}; Description: Open the startpage in the browser; Flags: runmaximized postinstall nowait; StatusMsg: Opening the startpage...; Parameters: {code:CreateLocalhostParams}

[Code]
function IExplorePath(Default: String): String;
begin
  Result := 'c:\program files\internet explorer\iexplore.exe';
  RegQueryStringValue(HKEY_LOCAL_MACHINE,
    'SOFTWARE\Classes\CLSID\{0002DF01-0000-0000-C000-000000000046}\LocalServer32',
    '', Result);
  Result:=RemoveQuotes(Result);
end;


76 - How to detect MS Access Version (2)

Article created: 31/05/04 20:00:18
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=76

Script detect if MS Access 97, 2000 or XP are installed, and read also path to each MSAccess.exe.

A page with checkboxesletuser select MS Access version, if a MS Access is not detected the checkbox is disabled.

;
; Inno Setup 4.1.7
;
;
[Setup]
AppName=MSAccessVersions
AppVerName=MSAccessVersions
DefaultDirName={pf}\MSAccessVersions
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true

[Code]
// Legenda
// 8.00.00.3512   Access 97
// 8.00.00.4122   Access 97 SR1
// 8.00.00.5903   Access 97 SR2
// 9.00.00.2719   Access 2000
// 9.00.00.3822   Access 2000 SR1
// 9.00.00.4506   Access 2000 SR2
// 10.00.2627.1   Access XP

var
    Acc97ver, Acc2Kver, AccXPver : String;
    Acc97Path, Acc2KPath, AccXPPath : String;

  rbAcc97: TRadioButton;
  rbAcc2K: TRadioButton;
  rbAccXP: TRadioButton;

Procedure ReadAccessVersions;
begin

  // Access 97
  if RegQueryStringValue(HKEY_LOCAL_MACHINE,
       'SOFTWARE\Microsoft\Office\8.0\Common\InstallRoot',
       '', Acc97Path) then
  begin
        Acc97Path := RemoveBackslash( Acc97Path ) + '\Office\MSAccess.exe';
        GetVersionNumbersString( Acc97Path, Acc97Ver );
  end;

  // Access 2000
  if RegQueryStringValue(HKEY_LOCAL_MACHINE,
       'SOFTWARE\Microsoft\Office\9.0\Access\InstallRoot',
       'Path', Acc2KPath) then
  begin
        Acc2KPath := RemoveBackslash( Acc2KPath ) + '\MSAccess.exe';
        GetVersionNumbersString( Acc2KPath, Acc2KVer );
  end;

  // Access XP
  if RegQueryStringValue(HKEY_LOCAL_MACHINE,
       'SOFTWARE\Microsoft\Office\10.0\Access\InstallRoot',
       'Path', AccXPPath) then
  begin
        AccXPPath := RemoveBackslash( AccXPPath ) + '\MSAccess.exe';
        GetVersionNumbersString( AccXPPath, AccXPVer );
  end;

end;

function InitializeSetup : Boolean;
begin
    // read Access Version from registry
  ReadAccessVersions;

    Result := true;
end;

function ShowAccessVersions( BackClicked: Boolean): Boolean;
var
  Next: Boolean;

begin

  ScriptDlgPageSetCaption('Access Versions');
  ScriptDlgPageSetSubCaption1('Access Versions Detected');
    ScriptDlgPageSetSubCaption2('');

    { rbAcc97 }
    rbAcc97 := TRadioButton.Create(WizardForm.ScriptDlgPanel);
    with rbAcc97 do
    begin
        Parent := WizardForm.ScriptDlgPanel;
        Left := 32;
        Top := 32;
        Width := 370;
        Height := 25;
        Caption := 'Access 97';
        TabOrder := 0;
    end;

    { rbAcc2K }
    rbAcc2K := TRadioButton.Create(WizardForm.ScriptDlgPanel);
    with rbAcc2K do
    begin
        Parent := WizardForm.ScriptDlgPanel;
        Left := 32;
        Top := 80;
        Width := 370;
        Height := 25;
        Caption := 'Access 2000';
        TabOrder := 1;
    end;

    { rbAccXP }
    rbAccXP := TRadioButton.Create(WizardForm.ScriptDlgPanel);
    with rbAccXP do
    begin
        Parent := WizardForm.ScriptDlgPanel;
        Left := 32;
        Top := 128;
        Width := 370;
        Height := 25;
        Caption := 'Access XP';
        Checked := True;
        TabOrder := 2;
        TabStop := True;
    end;

    if Length(AccXPver) = 0 then
    begin
        rbAccXP.Enabled := false; rbAccXP.Checked := false
    end
    else begin
        rbAccXP.Checked := true; rbAccXP.Caption := 'Access XP ' + AccXPPath;
    end;

    if Length(Acc2Kver) = 0 then
    begin
        rbAcc2K.Enabled := false; rbAcc2K.Checked := false
    end
    else begin
        rbAcc2K.Checked := true; rbAcc2K.Caption := 'Access 2000 ' + Acc2KPath;
    end;

    if Length(Acc97ver) = 0 then
    begin
        rbAcc97.Enabled := false; rbAcc97.Checked := false
    end
    else begin
        rbAcc97.Checked := true; rbAcc97.Caption := 'Access 97 ' + Acc97Path;
    end;

    Next := ScriptDlgPageProcessCustom();

    // See NextButtonClick and BackButtonClick: return True if the click should be allowed
    if not BackClicked then
        Result := Next
    else
        Result := not Next;
    // Close the wizard page. Do a FullRestore only if the click (see above) is not allowed
    ScriptDlgPageClose(not Result);

end;

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
begin
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpReady)) then begin
    // Insert a custom wizard page between two non custom pages
    Result := ShowAccessVersions( BackClicked );
  end
  else
    Result := True;
end;

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


77 - How to get Date and Time of a file

Article created: 31/05/04 23:46:31
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=77

It is possible to get Date and Time of file, script read Creation Time, Access Time and Modification Time.

To compile this sample you need LangLib.iss

Portion of code Renato Cazzaro

Code for Inno Setup versione 5 is How to get Date and Time of a file for IS 5

;
; Inno Setup 4.1.7
;
;
; Portion of code Renato Cazzaro
; http://news.jrsoftware.org/news/innosetup.code/msg03380.html
;

[Setup]
AppName=FileTime
AppVerName=FileTime
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\FileTime
DisableStartupPrompt=true

#include "LangLib.iss"

[Code]

type

TFileTime = Record
    lowdatatime: longint;
    highdatatime: longint;
End;

TSystemTime = record
    wYear : Word;
    wMonth : Word;
    wDayOfWeek : Word;
    wDay : Word;
    wHour : Word;
    wMinute : Word;
    wSecond : Word;
    wMilliseconds : Word;
end;

function GetFileTime(hfile: longint; var lpcreation,lpaccess,lpwrite:TFileTime): boolean;
external 'GetFileTime@kernel32.dll stdcall';

function FileTimeToSystemTime(var tftm: TFileTime; var systm: TSystemTime): boolean;
external 'FileTimeToSystemTime@kernel32.dll stdcall';

function FileTimeToLocalFileTime( utct: TFileTime; var loct: TFileTime): boolean;
external 'FileTimeToLocalFileTime@kernel32.dll stdcall';

function GetDateFormat(Locale: Integer;dwFlags: LongInt;
   var lpDate: TSystemTime; lpFormat: PChar; lpDateStr: String;
   cchDate: Integer): Integer; external
   'GetDateFormatA@kernel32.dll';

function GetTimeFormat(Locale: Integer;dwFlags: LongInt;
   var lpTime: TSystemTime;lpFormat: PChar;lpTimeStr: String;
   cchDate: Integer): Integer; external
   'GetTimeFormatA@kernel32.dll';

function FormattedTime( t: TSystemTime ) : String;
var
    s, fmt: String;
begin

        fmt := StringOfChar(' ',64);
        GetDateFormat( GetUserDefaultLCID , DATE_LONGDATE, t, '' , fmt ,63);
        fmt := CastIntegerToString( CastStringToInteger(fmt) );

        s := fmt;

        fmt := StringOfChar(' ',64);
        GetTimeFormat( GetUserDefaultLCID , 0, t , '' , fmt ,63);
        fmt := CastIntegerToString( CastStringToInteger(fmt) );

        s := s + ' ' + fmt;

    Result := s;
end;

Function GetFileSysTime( nomef: String ; var stime: String; idx : Integer  ): TSystemTime;
Var
    fs: TFileStream;
    risul: TSystemTime;
    creat, access, write, local: TFileTime;
Begin
    fs := TFileStream.Create(nomef,fmOpenRead);
    GetFileTime(fs.handle,creat,access,write);
    fs.Free;
    if idx = 1 then FileTimeToLocalFileTime(creat,local);
    if idx = 2 then FileTimeToLocalFileTime(access,local);
    if idx = 3 then FileTimeToLocalFileTime(write,local);
    FileTimeToSystemTime(local,risul);
    stime := FormattedTime( risul );
    Result := risul;
End;

var
  lbFile: TLabel;
  lbDateCreated: TLabel;
  lbDateModified: TLabel;
  Label2: TLabel;
  butChoose: TButton;
  edFile: TEdit;
  edCreTime: TEdit;
  edModTime: TEdit;
  edAccessTime: TEdit;

procedure ChooseFile(Sender: TObject);
var
    filename, s : String;
    t  : TSystemTime;
begin

    if GetOpenFileName( 'Choose a file', filename,
        GetShellFolder( false, sfDocs ) , 'Text files (*.txt)|*.txt|All files (*.*)|*.*' , '*.txt') then
    begin

        edFile.text := filename;
        t := GetFileSysTime(filename, s, 1 );
        edCreTime.text := s;
        t := GetFileSysTime(filename, s, 2 );
    edAccessTime.text := s;
    t := GetFileSysTime(filename, s, 3 );
    edModTime.text := s;

    end;

end;

{ ScriptDlgPages }

function ScriptDlgPages(CurPage: Integer; BackClicked: Boolean): Boolean;
var
  Next, NextOK: Boolean;
  CurSubPage: Integer;
begin
  { place subpages between 'Welcome'- and 'SelectDir' page }
  if (not BackClicked and (CurPage = wpWelcome)) or (BackClicked and (CurPage = wpSelectDir)) then
  begin
    { find startpage }
    if not BackClicked then
      CurSubPage := 0
    else
      CurSubPage := 1;

    { iterate through all subpages }
    while (CurSubPage >= 0) and (CurSubPage <= 1) and not Terminated do
    begin
      ScriptDlgPageOpen();
      ScriptDlgPageClearCustom();

      { insert subpage }
      case CurSubPage of
        0:  // custompage 1
          begin
            ScriptDlgPageSetCaption('Time of a file');
            ScriptDlgPageSetSubCaption1('Date and time of a file');
            ScriptDlgPageSetSubCaption2('');

            { lbFile }
            lbFile := TLabel.Create(WizardForm.ScriptDlgPanel);
            with lbFile do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 56;
              Width := 415;
              Height := 13;
              AutoSize := False;
              Caption := 'File selected:';
            end;

            { lbDateCreated }
            lbDateCreated := TLabel.Create(WizardForm.ScriptDlgPanel);
            with lbDateCreated do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 104;
              Width := 199;
              Height := 13;
              AutoSize := False;
              Caption := 'Creation Time';
            end;

            { lbDateModified }
            lbDateModified := TLabel.Create(WizardForm.ScriptDlgPanel);
            with lbDateModified do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 152;
              Width := 199;
              Height := 13;
              AutoSize := False;
              Caption := 'Modification Time';
            end;

            { Label2 }
            Label2 := TLabel.Create(WizardForm.ScriptDlgPanel);
            with Label2 do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 192;
              Width := 199;
              Height := 13;
              AutoSize := False;
              Caption := 'Access Time';
            end;

            { butChoose }
            butChoose := TButton.Create(WizardForm.ScriptDlgPanel);
            with butChoose do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 88;
              Top := 16;
              Width := 233;
              Height := 25;
              Caption := 'Choose a file...';
              TabOrder := 0;
              OnClick := @ChooseFile;
            end;

            { edFile }
            edFile := TEdit.Create(WizardForm.ScriptDlgPanel);
            with edFile do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 72;
              Width := 409;
              Height := 21;
              TabStop := False;
              ReadOnly := True;
              TabOrder := 1;
            end;

            { edCreTime }
            edCreTime := TEdit.Create(WizardForm.ScriptDlgPanel);
            with edCreTime do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 120;
              Width := 201;
              Height := 21;
              TabStop := False;
              ReadOnly := True;
              TabOrder := 2;
            end;

            { edModTime }
            edModTime := TEdit.Create(WizardForm.ScriptDlgPanel);
            with edModTime do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 168;
              Width := 201;
              Height := 21;
              TabStop := False;
              ReadOnly := True;
              TabOrder := 3;
            end;

            { edAccessTime }
            edAccessTime := TEdit.Create(WizardForm.ScriptDlgPanel);
            with edAccessTime do
            begin
              Parent := WizardForm.ScriptDlgPanel;
              Left := 0;
              Top := 208;
              Width := 201;
              Height := 21;
              TabStop := False;
              ReadOnly := True;
              TabOrder := 4;
            end;

            Next := ScriptDlgPageProcessCustom();
            NextOK := True;
          end;

        1: // custompage n
          begin
            { include other forms here }
          end;
      end;

      { check sub-page navigation }
      if Next then
      begin
        if NextOK then
          CurSubPage := CurSubPage + 1;
      end
      else
        CurSubPage := CurSubPage - 1;
    end;

    { check main-page navigation }
    if not BackClicked then
      Result := Next
    else
      Result := not Next;
    ScriptDlgPageClose(not Result);
  end

  { return default }
  else
    Result := True;
end;

{ NextButtonClick }

function NextButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, False);
end;

{ BackButtonClick }

function BackButtonClick(CurPage: Integer): Boolean;
begin
  Result := ScriptDlgPages(CurPage, True);
end;


78 - How to create a virtual FTP directory for IIS

Article created: 16/06/04 16:13:47
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=78

Author: Peter-John Whitmore wrote

I have a problem with your solution How to create a virtual directory for IIS when scriptblocking is activated. so here is how I do it without vbs.

[Code]

procedure CreateVirtualFTPDirectory(sName : String);
var
 vDir, WebSite, WebServer, WebRoot : Variant;
 ServerAddress, W3svcNumber, ApplicationPath : string;
 svcNumber, appPath : Variant;
 sPath : String;
begin
        sPath := ExpandConstant('{app}\data');
        ServerAddress := '127.0.0.1';
        W3svcNumber   := '1';
        ApplicationPath := 'root';

        WebSite := CreateOleObject('IISNamespace');

        ObjectToGet := ServerAddress + '/msftpsvc';
        WebSite := WebSite.GetObject('IIsFtpService', ObjectToGet );

        svcNumber := W3svcNumber;
        WebServer := WebSite.GetObject('IIsFtpServer', svcNumber);

        appPath := ApplicationPath;
        WebRoot := WebServer.GetObject('IIsFtpVirtualDir', appPath);

        try
                WebRoot.delete('IIsFtpVirtualDir', sName);
        except
        end;

        VDir := WebRoot.Create('IIsFtpVirtualDir', sName);
        VDir.AccessRead := True;
        VDir.AccessWrite := True;
        VDir.Path := sPath;
        VDir.SetInfo();
end;


79 - How to close an application posting a QUIT message

Article created: 08/07/04 16:16:52
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=79

Author: Stefan Kniffka

;How to close / Kill a Application
;Author: Stefan Kniffka
;Compiler: ISX 4.2.1
[Setup]
AppName=Killer
AppVerName=Killer
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\Drvtype
DisableStartupPrompt=true

[_ISTool]
EnableISX=true

[Code]

function InitializeSetup: Boolean;
var
   WindowNumber, closeWindow:integer;

begin
   closeWindow:=16; //CloseValue
   WindowNumber:=FindWindowByWindowName('test'); //Find Number by Name
   //WindowNumber:=FindWindowByClass('');		//or by Class
   PostMessage(WindowNumber,closeWindow,0,0);	//close send to Application-Window

   //next step for installation ;-)
   result:=true;
end;


80 - How to detect if JET engine is installed

Article created: 04/08/04 14:20:47
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=80

Author: Truckle, Andrew J

Returns True if either JET 3.5 or JET 4.0 is installed.

The article How to obtain the latest service pack for the Microsoft Jet 4.0 Database Engine tells how one could locate the msjet40.dll in the system folder and decode it's version string. That would be another way of testing if JET is installed.

// Returns True if either JET 3.5 or JET 4.0 is installed
function IsJETInstalled(): Boolean;
begin
   Result := RegKeyExists(HKLM, 'Software\Microsoft\Jet\3.5\Engines')
or
   RegKeyExists(HKLM, 'Software\Microsoft\Jet\4.0\Engines');
end;

In initializesetup:

// Let Setup run (if JET is installed)
if not IsJETInstalled then
begin
    MsgBox( ExpandConstant('{cm:InstallJETFirst}'), mbInformation, MB_OK );
    Result := False;
end
else
   Result := True;


81 - Modified the Test Events sample so that it works with Inno Setup 5

Article created: 03/01/05 19:13:00
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=81

Author: Jeff Weir

Modified the Test Events sample so that it works with Inno Setup 5

[Setup]
OutputBaseFilename=TestEventsSetup
AppName=TestEvents
AppVerName=TestEvents V1
DefaultDirName={pf}\TestEvents
AppID=TestEevents
ShowTasksTreeLines=false
RestartIfNeededByRun=false
UserInfoPage=true
DefaultGroupName=TestEvents
InfoAfterFile=.\InfoAfterFile.txt
InfoBeforeFile=.\InfoBeforeFile.txt
LicenseFile=.\LicenseFile.txt
OutputDir=.\

[Files]
Source: Test Events.iss; DestDir: {app}

[Icons]
Name: {group}\Uninstall Test Events; Filename: {uninstallexe}
Name: {group}\Test Events; Filename: {app}\Test Events.iss; WorkingDir: {app}; Comment: Test Events; Flags: createonlyiffileexists

[_ISTool]
EnableISX=true
UseAbsolutePaths=false

[Code]
var
    mcLogFile: String;

function InitializeSetup(): Boolean;
// return False to abort Setup
begin
    mcLogFile := 'C:\Log_IS_Events.txt';
    DeleteFile( mcLogFile);
    SaveStringToFile(mcLogFile, 'In function InitializeSetup' + Chr(13), True);
    Result := True;
end;

procedure InitializeWizard();
// Use this event function to make changes to the wizard or wizard pages at startup.
// You can't use the InitializeSetup event function for this since at the time it is triggered,
// the wizard form does not yet exist.
begin
    SaveStringToFile(mcLogFile, 'In procedure InitializeWizard' + Chr(13), True);
end;

procedure DeInitializeSetup();
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure DeInitializeSetup' + Chr(13), True);
end;

procedure CurStepChanged(CurStep: TSetupStep);
// return False to abort Setup
begin
    case CurStep of
        ssInstall :
          SaveStringToFile(mcLogFile, ' : ssInstall' + Chr(13), True);
        ssPostInstall :
          SaveStringToFile(mcLogFile, ' : ssPostInstall' + Chr(13), True);
        ssDone :
          SaveStringToFile(mcLogFile, ' : ssDone' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function NextButtonClick(CurPage: Integer): Boolean;
// return False to surpress the click on the Next button
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function NextButtonClick ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

function BackButtonClick(CurPage: Integer): Boolean;
// return False to surpress the click on the Back button
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function BackButtonClick ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

function ShouldSkipPage(CurPage: Integer): Boolean;
// return True to skip the CurPage page
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function SkipCurPage ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := False;
end;

procedure CurPageChanged(CurPage: Integer);
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure CurPageChanged ' + IntToStr(CurPage), True);
    case CurPage of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function CheckPassword(Password: String): Boolean;
// If Setup finds the CheckPassword event function in the Pascal script, it automatically
// displays the Password page and calls CheckPassword to check passwords.
// Return True to accept the password and False to reject it.
// When using a password, it's important to keep in mind that since no encryption is used and
// the source code to Inno Setup is freely available, it would not be too difficult for an experienced
// individual to remove the password protection from an installation.
// Use this only as a 'deterrent' to keep unauthorized people out of your installations.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function CheckPassword ' + Password + Chr(13), True);
    Result := True;
end;

function NeedRestart(): Boolean;
// return True to instruct Setup to prompt the user to restart the system at the end of a successful installation.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function NeedRestart' + Chr(13), True);
    Result := False;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
// If Setup finds the UpdateReadyMemo event function in the Pascal script, it is called automatically
//  when the Ready to Install wizard page becomes the active page.
// It should return the text to be displayed in the settings memo on the Ready to Install wizard page
// as a single string with lines separated by the NewLine parameter.
// Parameter Space contains a string with spaces. Setup uses this string to indent settings.
// The other parameters contain the (possibly empty) strings that Setup would have used as the setting sections.
// The MemoDirInfo parameter for example contains the string for the Selected Directory section.
// return False to abort Setup
var
    cTemp: String;
begin
    SaveStringToFile(mcLogFile, 'In function UpdateReadyMemo' + Chr(13), True);
    cTemp := MemoUserInfoInfo + NewLine;
    cTemp := cTemp + MemoDirInfo + NewLine;
    cTemp := cTemp + MemoTypeInfo + NewLine;
    cTemp := cTemp + MemoComponentsInfo + NewLine;
    cTemp := cTemp + MemoGroupInfo + NewLine;
    cTemp := cTemp + MemoTasksInfo + NewLine;
    Result := cTemp;
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
// To store user settings entered on custom wizard pages, place a RegisterPreviousData event function
// in the Pascal script and call SetPreviousData(PreviousDataKey, ...) inside it, once per setting.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In procedure PreviousDataKey' + Chr(13), True);
end;

function CheckSerial(Serial: String): Boolean;
// If Setup finds the CheckSerial event function in the Pascal script, a serial number field
// with automatically appear on the User Info wizard page (which must be enabled using
// UserInfoPage=yes in your [Setup] section!).
// Return True to accept the serial number and False to reject it.
// When using serial numbers, it's important to keep in mind that since no encryption is used and
// the source code to Inno Setup is freely available, it would not be too difficult for an
// experienced individual to remove the serial number protection from an installation.
// Use this only as a convienience to the end user and double check the entered serial number
// (stored in the {userinfoserial} constant) in your application.
// return False to abort Setup
begin
    SaveStringToFile(mcLogFile, 'In function CheckSerial ' + Serial + Chr(13), True);
    Result := True;
end;

// Here's the list of constants used by these functions:

// CurStep values
// ssInstall, ssPostInstall, ssDone

// CurPage values
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir,
// wpSelectComponents, wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing,
// wpInstalling, wpInfoAfter, wpFinished

// None of these functions are required to be present in a Pascal script.

// SkipCurPage isn't called if Setup already determined that the page should be skipped or
// for the Welcome page or for the Installing page.


82 - How to check for .NET framework for Inno Setup 5

Article created: 10/01/05 19:00:02
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=82

Author: Matej Cerk

Original code modified from How to check for .NET framework for Inno Setup 4.

[Setup]
AppName=NETFramewrok
AppVerName=NETFramewrok
DefaultDirName={pf}\NETFramewrok
DisableStartupPrompt=true

[Code]

function InitializeSetup(): Boolean;
var
    ErrorCode: Integer;
    NetFrameWorkInstalled : Boolean;
    Result1 : Boolean;
begin

           NetFrameWorkInstalled := RegKeyExists(HKLM,'SOFTWARE\Microsoft\.NETFramework\policy\v1.0');
           if NetFrameWorkInstalled =true then
           begin
              Result := true;
           end;
           if NetFrameWorkInstalled = false then
           begin
               NetFrameWorkInstalled := RegKeyExists(HKLM,'SOFTWARE\Microsoft\.NETFramework\policy\v1.1');
               if NetFrameWorkInstalled =true then
               begin
                  Result := true;
               end;

               if NetFrameWorkInstalled =false then
               begin
                         Result1 := MsgBox('This setup requires the .NET Framework. Please download and install the .NET Framework and run this setup again. Do you want to download the framwork now?',
                              mbConfirmation, MB_YESNO) = idYes;
                         if Result1 =false then
                         begin
                            Result:=false;
                         end
                         else
                         begin
                              Result:=false;
                                //inno setup 5
                                ShellExec('open', 'http://download.microsoft.com/download/a/a/c/aac39226-8825-44ce-90e3-bf8203e74006/dotnetfx.exe','','',SW_SHOWNORMAL,ewNoWait,ErrorCode);
                                //inno setup 4
                                //InstShellExec('http://download.microsoft.com/download/a/a/c/aac39226-8825-44ce-90e3-bf8203e74006/dotnetfx.exe','','',SW_SHOWNORMAL,ErrorCode);
                        end;
               end;
          end;
end;


83 - Test Events for Inno Setup 5 with Uninstall events logged

Article created: 23/01/05 14:00:25
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=83

Author: Wolfgang Weh

I've updated the sample script which logs all event functions to work with InnoSetup V5.

The uninstall events are now also logged.

[Setup]
OutputBaseFilename=TestEventsSetup
AppName=TestEvents
AppVerName=TestEvents V1.1
DefaultDirName={pf}\TestEvents
AppID=TestEvents
ShowTasksTreeLines=false
RestartIfNeededByRun=false
UserInfoPage=true
DefaultGroupName=TestEvents
InfoAfterFile=.\InfoAfterFile.txt
InfoBeforeFile=.\InfoBeforeFile.txt
LicenseFile=.\LicenseFile.txt
OutputDir=.\
WindowVisible=true
UninstallLogMode=append

[Files]
Source: Test Events.iss; DestDir: {app}
Source: LicenseFile.txt; DestDir: {app}
Source: InfoBeforeFile.txt; DestDir: {app}
Source: InfoAfterFile.txt; DestDir: {app}

[Icons]
Name: {group}\Uninstall Test Events; Filename: {uninstallexe}
Name: {group}\Test Events; Filename: {app}\Test Events.iss; WorkingDir: {app}; Comment: Test Events; Flags: createonlyiffileexists

[Messages]
BeveledLabel=Test Events Setup

[Code]
var
    mcLogFile: String;

//  Install Event Functions
//==========================

function InitializeSetup(): Boolean;
// function InitializeSetup(): Boolean;
// Called during Setup's initialization. Return False to abort Setup, True otherwise.
begin
    mcLogFile := 'C:\Log_IS_Events.txt';
    DeleteFile( mcLogFile);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, mcLogFile + ' - logfile of InnoSetups install and uninstall events.' + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'Start installation: ' + GetDateTimeString('dd/mm/yyyy hh:nn:ss', '.', ':') + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'In function InitializeSetup' + Chr(13), True);
    Result := True;
end;

procedure InitializeWizard();
// procedure InitializeWizard();
// Use this event function to make changes to the wizard or wizard pages at startup.
// You can't use the InitializeSetup event function for this since at the time
// it is triggered, the wizard form does not yet exist.
begin
    SaveStringToFile(mcLogFile, 'In procedure InitializeWizard' + Chr(13), True);
end;

procedure DeInitializeSetup();
// procedure DeinitializeSetup();
// Called just before Setup terminates. Note that this function is called even
// if the user exits Setup before anything is installed.
begin
    SaveStringToFile(mcLogFile, 'In procedure DeInitializeSetup' + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'End installation: ' + GetDateTimeString('dd/mm/yyyy hh:nn:ss', '.', ':') + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
end;

procedure CurStepChanged(CurStep: TSetupStep);
// procedure CurStepChanged(CurStep: TSetupStep);
// You can this event function to perform your own pre-install and post-install tasks.
// Called with CurStep=ssInstall just before the actual installation starts,
// with CurStep=ssPostInstall just after the actual installation finishes,
// and with CurStep=ssDone just before Setup terminates after a successful install.
begin
    SaveStringToFile(mcLogFile, 'In procedure CurStepChanged, CurStep=', True);
    case CurStep of
        ssInstall :
          SaveStringToFile(mcLogFile, ' ssInstall' + Chr(13), True);
        ssPostInstall :
          SaveStringToFile(mcLogFile, ' ssPostInstall' + Chr(13), True);
        ssDone :
          SaveStringToFile(mcLogFile, ' ssDone' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' no idea' + Chr(13), True);
    end;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
// function NextButtonClick(CurPageID: Integer): Boolean;
// Called when the user clicks the Next button.
// If you return True, the wizard will move to the next page;
// if you return False, it will remain on the current page (specified by CurPageID).
// Note that this function is called on silent installs as well,
// even though there is no Next button that the user can click.
// Setup instead simulates "clicks" on the Next button.
// On a silent install, if your NextButtonClick function returns False prior to installation starting,
// Setup will exit automatically.

// CurPageID values for predefined wizard pages
//----------------------------------------------
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir, wpSelectComponents,
// wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing, wpInstalling, wpInfoAfter, wpFinished

begin
    SaveStringToFile(mcLogFile, 'In function NextButtonClick, CurPageID=' + IntToStr(CurPageID), True);
    case CurPageID of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

function BackButtonClick(CurPageID: Integer): Boolean;
// function BackButtonClick(CurPageID: Integer): Boolean;
// Called when the user clicks the Back button.
// If you return True, the wizard will move to the previous page;
// if you return False, it will remain on the current page (specified by CurPageID).

// CurPageID values for predefined wizard pages
//----------------------------------------------
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir, wpSelectComponents,
// wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing, wpInstalling, wpInfoAfter, wpFinished

begin
    SaveStringToFile(mcLogFile, 'In function BackButtonClick, CurPageID=' + IntToStr(CurPageID), True);
    case CurPageID of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := True;
end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
// procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
// Called when the user clicks the Cancel button or clicks the window's Close button.
// The Cancel parameter specifies whether normal cancel processing should occur; it defaults to True.
// The Confirm parameter specifies whether an "Exit Setup?" message box should be displayed; it usually defaults to True.
// If Cancel is set to False, then the value of Confirm is ignored.

// CurPageID values for predefined wizard pages
//----------------------------------------------
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir, wpSelectComponents,
// wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing, wpInstalling, wpInfoAfter, wpFinished

var
  cTemp: String;
begin
    SaveStringToFile(mcLogFile, 'In function CancelButtonClick, CurPageID=' + IntToStr(CurPageID), True);
    if Cancel then cTemp:='True' else cTemp:='False';
    SaveStringToFile(mcLogFile, ', Cancel=' + cTemp, True);
    if Confirm then cTemp:='True' else cTemp:='False';
    SaveStringToFile(mcLogFile, ', Confirm=' + cTemp, True);
    case CurPageID of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
// function ShouldSkipPage(PageID: Integer): Boolean;
// The wizard calls this event function to determine whether or not a particular page
// (specified by PageID) should be shown at all.
// If you return True, the page will be skipped;
// if you return False, the page may be shown.
// Note: This event function isn't called for the wpWelcome, wpPreparing, and wpInstalling pages,
// not for pages that Setup has already determined should be skipped
// (for example, wpSelectComponents in an install containing no components).
begin
    SaveStringToFile(mcLogFile, 'In function ShouldSkipPage, PageID=' + IntToStr(PageID), True);
    case PageID of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
    Result := False;
end;

procedure CurPageChanged(CurPageID: Integer);
// procedure CurPageChanged(CurPageID: Integer);
// Called after a new wizard page (specified by CurPageID) is shown.

// CurPageID values for predefined wizard pages
//----------------------------------------------
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir, wpSelectComponents,
// wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing, wpInstalling, wpInfoAfter, wpFinished

begin
    SaveStringToFile(mcLogFile, 'In procedure CurPageChanged, CurPageID=' + IntToStr(CurPageID), True);
    case CurPageID of
        wpWelcome :
          SaveStringToFile(mcLogFile, ' : wpWelcome' + Chr(13), True);
        wpLicense :
          SaveStringToFile(mcLogFile, ' : wpLicense' + Chr(13), True);
        wpPassword :
          SaveStringToFile(mcLogFile, ' : wpPassword' + Chr(13), True);
        wpInfoBefore :
          SaveStringToFile(mcLogFile, ' : wpInfoBefore' + Chr(13), True);
        wpUserInfo :
          SaveStringToFile(mcLogFile, ' : wpUserInfo' + Chr(13), True);
        wpSelectDir :
          SaveStringToFile(mcLogFile, ' : wpSelectDir' + Chr(13), True);
        wpSelectComponents :
          SaveStringToFile(mcLogFile, ' : wpSelectComponents' + Chr(13), True);
        wpSelectProgramGroup :
          SaveStringToFile(mcLogFile, ' : wpSelectProgramGroup' + Chr(13), True);
        wpSelectTasks :
          SaveStringToFile(mcLogFile, ' : wpSelectTasks' + Chr(13), True);
        wpReady :
          SaveStringToFile(mcLogFile, ' : wpReady' + Chr(13), True);
        wpPreparing :
          SaveStringToFile(mcLogFile, ' : wpPreparing' + Chr(13), True);
        wpInstalling :
          SaveStringToFile(mcLogFile, ' : wpInstalling' + Chr(13), True);
        wpInfoAfter :
          SaveStringToFile(mcLogFile, ' : wpInfoAfter' + Chr(13), True);
        wpFinished :
          SaveStringToFile(mcLogFile, ' : wpFinished' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' : no idea' + Chr(13), True);
    end;
end;

function CheckPassword(Password: String): Boolean;
// function CheckPassword(Password: String): Boolean;
// If Setup finds the CheckPassword event function in the Pascal script, it automatically
// displays the Password page and calls CheckPassword to check passwords.
// Return True to accept the password and False to reject it.
//	To avoid storing the actual password inside the compiled [Code] section which is stored inside Setup,
// you should use comparisons by hash only: calculate the MD5 hash of your password yourself and then
// compare that to GetMD5OfString(Password).
// This way the actual value of the password remains protected.
//
// Note: if you have a CheckPassword event function and your users run Setup with both
// the "/PASSWORD=" and "/SILENT" command line parameters set, your CheckPassword function
// will be called *before* any other event function is called, including InitializeSetup.
begin
    SaveStringToFile(mcLogFile, 'In function CheckPassword, Password=' + Password + Chr(13), True);
    Result := True;
end;

function NeedRestart(): Boolean;
// function NeedRestart(): Boolean;
// Return True to instruct Setup to prompt the user to restart the system at the
// end of a successful installation, False otherwise.
begin
    SaveStringToFile(mcLogFile, 'In function NeedRestart' + Chr(13), True);
    Result := False;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
// function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
//                          MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
// If Setup finds the UpdateReadyMemo event function in the Pascal script, it is called automatically
// when the Ready to Install wizard page becomes the active page.
// It should return the text to be displayed in the settings memo on the Ready to Install wizard page
// as a single string with lines separated by the NewLine parameter.
// Parameter Space contains a string with spaces. Setup uses this string to indent settings.
// The other parameters contain the (possibly empty) strings that Setup would have used as the setting sections.
// The MemoDirInfo parameter for example contains the string for the Selected Directory section.
var
    cTemp: String;
begin
    SaveStringToFile(mcLogFile, 'In function UpdateReadyMemo' + Chr(13), True);
    cTemp := MemoUserInfoInfo + NewLine;
    cTemp := cTemp + MemoDirInfo + NewLine;
    cTemp := cTemp + MemoTypeInfo + NewLine;
    cTemp := cTemp + MemoComponentsInfo + NewLine;
    cTemp := cTemp + MemoGroupInfo + NewLine;
    cTemp := cTemp + MemoTasksInfo + NewLine;
    Result := cTemp;
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
// procedure RegisterPreviousData(PreviousDataKey: Integer);
// To store user settings entered on custom wizard pages, place a RegisterPreviousData event function
// in the Pascal script and call SetPreviousData(PreviousDataKey, ...) inside it, once per setting.
begin
    SaveStringToFile(mcLogFile, 'In procedure PreviousDataKey, PreviousDataKey=' + Chr(13), True);
end;

function CheckSerial(Serial: String): Boolean;
// function CheckSerial(Serial: String): Boolean;
// If Setup finds the CheckSerial event function in the Pascal script, a serial number field will
// automatically appear on the User Info wizard page (which must be enabled using UserInfoPage=yes in your [Setup] section!).
// Return True to accept the serial number and False to reject it.
// When using serial numbers, it's important to keep in mind that since no encryption is used and the source code to
// Inno Setup is freely available, it would not be too difficult for an experienced individual to remove the serial
// number protection from an installation. Use this only as a convienience to the end user and double check the entered
// serial number (stored in the {userinfoserial} constant) in your application.
begin
    SaveStringToFile(mcLogFile, 'In function CheckSerial, Serial=' + Serial + Chr(13), True);
    Result := True;
end;

function GetCustomSetupExitCode: Integer;
// function GetCustomSetupExitCode: Integer;
// Return a non zero number to instruct Setup to return a custom exit code.
// This function is only called if Setup was successfully run to completion and
// the exit code would have been 0. Also see Setup Exit Codes.
begin
    SaveStringToFile(mcLogFile, 'In function GetCustomSetupExitCode' + Chr(13), True);
    Result := 0;
end;

//  UNinstall Event Functions
//============================
function InitializeUninstall(): Boolean;
// function InitializeUninstall(): Boolean;
// Return False to abort Uninstall, True otherwise.
begin
    mcLogFile := 'C:\Log_IS_Events.txt';
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, mcLogFile + ' - logfile of InnoSetups install and uninstall events.' + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'Start Uninstallation: ' + GetDateTimeString('dd/mm/yyyy hh:nn:ss', '.', ':') + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'In function InitializeUninstall' + Chr(13), True);
    Result := True;
end;

procedure DeinitializeUninstall();
// procedure DeinitializeUninstall();
begin
    SaveStringToFile(mcLogFile, 'In function DeinitializeUninstall' + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
    SaveStringToFile(mcLogFile, 'End Uninstallation: ' + GetDateTimeString('dd/mm/yyyy hh:nn:ss', '.', ':') + Chr(13), True);
    SaveStringToFile(mcLogFile, Chr(13), True);
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
// procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
// You can this event function to perform your own pre-UNinstall and post-UNinstall tasks.
begin
    SaveStringToFile(mcLogFile, 'In procedure CurUninstallStepChanged, CurUninstallStep=', True);
    case CurUninstallStep of
        usAppMutexCheck :
          SaveStringToFile(mcLogFile, ' usAppMutexCheck' + Chr(13), True);
        usUninstall :
          SaveStringToFile(mcLogFile, ' usUninstall' + Chr(13), True);
        usPostUninstall :
          SaveStringToFile(mcLogFile, ' usPostUninstall' + Chr(13), True);
        usDone :
          SaveStringToFile(mcLogFile, ' usDone' + Chr(13), True);
    else
        SaveStringToFile(mcLogFile, ' no idea' + Chr(13), True);
    end;
end;

function UninstallNeedRestart(): Boolean;
// function UninstallNeedRestart(): Boolean;
// Return True to instruct Uninstall to prompt the user to restart the system at
// the end of a successful uninstallation, False otherwise.
begin
    SaveStringToFile(mcLogFile, 'In function UninstallNeedRestart' + Chr(13), True);
    Result := False;
end;

//  Here's the list of constants used by these functions
//=======================================================

// CurStep values
//----------------
// ssInstall, ssPostInstall, ssDone

// CurUninstallStep values
//-------------------------
// usAppMutexCheck, usUninstall, usPostUninstall, usDone

// CurPageID values for predefined wizard pages
//----------------------------------------------
// wpWelcome, wpLicense, wpPassword, wpInfoBefore, wpUserInfo, wpSelectDir, wpSelectComponents,
// wpSelectProgramGroup, wpSelectTasks, wpReady, wpPreparing, wpInstalling, wpInfoAfter, wpFinished

// None of these functions are required to be present in a Pascal script.


85 - How to put the current Java version at the beginning of the PATH

Article created: 12/04/05 21:21:42
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=85

Author: Bruce Ingalls

Articles used to write this code:

How to check for JDK

Native ISX procedures for PATH modification

Note that there is no code to uninstall %PATH% changes.

[Code]
const
  MIN_JAVA_VERSION = '1.4.0';
//* Getting Java version from registry *//
function getJavaVersion(): String;
var
  javaVersion: String;
begin
  javaVersion := '';
  RegQueryStringValue(HKLM, 'SOFTWARE\JavaSoft\Java Runtime Environment', 'CurrentVersion', javaVersion);
  GetVersionNumbersString(javaVersion, javaVersion);
  Result := javaVersion;
end;

function getJavaPath(javaVersion: String): String;
var
  javaHome: String;
begin
  RegQueryStringValue(HKLM, 'SOFTWARE\JavaSoft\Java Runtime Environment' + javaVersion, 'JavaHome', javaHome);
  Result := javaHome + '\bin';
end;

const
  // Modification method
  pmAddToBeginning = $1;      // Add dir to beginning of Path
  pmAddToEnd = $2;            // Add dir to end of Path
  pmAddAllways = $4;          // Add also if specified dir is already included in existing path
  pmAddOnlyIfDirExists = $8;  // Add only if specified dir actually exists
  pmRemove = $10;             // Remove dir from path
  pmRemoveSubdirsAlso = $20;  // Remove dir and all subdirs from path

  // Scope
  psCurrentUser = 1;          // Modify path for current user
  psAllUsers = 2;             // Modify path for all users

  // Error results
  mpOK = 0;                   // No errors
  mpMissingRights = -1;       // User has insufficient rights
  mpAutoexecNoWriteacc = -2;  // Autoexec can not be written (may be readonly)
  mpBothAddAndRemove = -3;    // User has specified that dir should both be removed from and added to path

{ Helper procedure: Split a path environment variable into individual dirnames }
procedure SplitPath(Path: string; var Dirs: TStringList);
var
  pos: integer;
  s: string;
begin
  Dirs.Clear;
  s := '';
  pos := 1;
  while (pos<=Length(Path)) do
  begin
    if (Path[pos]<>';') then
      s := s + Path[pos];
    if ((Path[pos]=';') or (pos=Length(Path))) then
    begin
      s := Trim(s);
      s := RemoveQuotes(s);
      s := Trim(s);
      if (s <> '') then
        Dirs.Add(s);
      s := '';
    end;
    Pos := Pos + 1;
  end;
end; // procedure SplitPath

{ Helper procedure: Concatenate individual dirnames into a path environment variable }
procedure ConcatPath(Dirs: TStringList; Quotes: boolean; var Path: string);
var
  Index, MaxIndex: integer;
  s: string;
begin
  MaxIndex := Dirs.Count-1;
  Path := '';
  for Index := 0 to MaxIndex do
  begin
    s := Dirs.Strings[Index];
    if ((Quotes) and (pos(' ',s) > 0)) then
      s := AddQuotes(s);
    Path := Path + s;
    if (Index < MaxIndex) then
      Path := Path + ';'
  end;
end; // procedure ConcatPath

{ Helper function: Modifies path environment string }
procedure ModifyPathString(OldPath, DirName: string; Method: integer; Quotes: boolean; var ResultPath: string);
var
  Dirs: TStringList;
  DirNotInPath: Boolean;
  i: integer;
begin
  // Create Dirs variable
  Dirs := TStringList.Create;

  // Remove quotes form DirName
  DirName := Trim(DirName);
  DirName := RemoveQuotes(DirName);
  DirName := Trim(DirName);

  // Split old path in individual directory names
  SplitPath(OldPath, Dirs);

  // Check if dir is allready in path
  DirNotInPath := True;
  for i:=Dirs.Count-1 downto 0 do
  begin
    if (uppercase(Dirs.Strings[i]) = uppercase(DirName)) then
      DirNotInPath := False;
  end;

  // Should dir be removed from existing Path?
  if ((Method and (pmRemove or pmRemoveSubdirsAlso)) > 0) then
  begin
    for i:=Dirs.Count-1 downto 0 do
    begin
      if (((Method and pmRemoveSubdirsAlso) > 0) and (pos(uppercase(DirName)+'', uppercase(Dirs.Strings[i])) = 1)) or
         (((Method and (pmRemove) or (pmRemoveSubdirsAlso)) > 0) and (uppercase(DirName) = uppercase(Dirs.Strings[i])))
      then
        Dirs.Delete(i);
    end;
  end;

  // Should dir be added to existing Path?
  if ((Method and (pmAddToBeginning or pmAddToEnd)) > 0) then
  begin
    // Add dir to path
    if (((Method and pmAddAllways) > 0) or DirNotInPath) then
    begin
      // Dir is not in path allready or should be added anyway
      if (((Method and pmAddOnlyIfDirExists) = 0) or (DirExists(DirName))) then
      begin
        // Dir actually exsists or should be added anyway
        if ((Method and pmAddToBeginning) > 0) then
          Dirs.Insert(0, DirName)
        else
          Dirs.Append(DirName);
      end;
    end;
  end;

  // Concatenate directory names into one single path variable
  ConcatPath(Dirs, Quotes, ResultPath);
  // Finally free Dirs object
  Dirs.Free;
end; // ModifyPathString

{ Helper function: Modify path on Windows 9x }
function ModifyPath9x(DirName: string; Method: integer): integer;
var
  AutoexecLines: TStringList;
  ActualLine: String;
  PathLineNos: TStringList;
  FirstPathLineNo: Integer;
  OldPath, ResultPath: String;
  LineNo, CharNo, Index: integer;

  TempString: String;
begin
  // Expect everything to be OK
  result := mpOK;

  // Create stringslists
  AutoexecLines := TStringList.Create;
  PathLineNos := TStringList.Create;

  // Read existing path
  OldPath := '';
  LoadStringFromFile('c:\Autoexec.bat', TempString);
  AutoexecLines.Text := TempString;
  PathLineNos.Clear;
  // Read Autoexec line by line
  for LineNo := 0 to AutoexecLines.Count - 1 do begin
    ActualLine := AutoexecLines.Strings[LineNo];
    // Check if line starts with "PATH=" after first stripping spaces and other "fill-chars"
    if Pos('=', ActualLine) > 0 then
    begin
      for CharNo := Pos('=', ActualLine)-1 downto 1 do
        if (ActualLine[CharNo]=' ') or (ActualLine[CharNo]=#9) then
          Delete(ActualLine, CharNo, 1);
      if Pos('@', ActualLine) = 1 then
        Delete(ActualLine, 1, 1);
      if (Pos('PATH=', uppercase(ActualLine))=1) or (Pos('SETPATH=', uppercase(ActualLine))=1) then
      begin
        // Remove 'PATH=' and add path to "OldPath" variable
        Delete(ActualLine, 1, pos('=', ActualLine));
        // Check if an earlier PATH variable is referenced, but there has been no previous PATH defined in Autoexec
        if (pos('%PATH%',uppercase(ActualLine))>0) and (PathLineNos.Count=0) then
          OldPath := ExpandConstant('{win}') + ';' + ExpandConstant('{win}')+'\COMMAND';
        if (pos('%PATH%',uppercase(ActualLine))>0) then
        begin
          ActualLine := Copy(ActualLine, 1, pos('%PATH%',uppercase(ActualLine))-1) +
                        OldPath +
                        Copy(ActualLine, pos('%PATH%',uppercase(ActualLine))+6, Length(ActualLine));
        end;
        OldPath := ActualLine;

        // Update list of line numbers holding path variables
        PathLineNos.Add(IntToStr(LineNo));
      end;
    end;
  end;

  // Save first line number in Autoexec.bat which modifies path environment variable
  if PathLineNos.Count > 0 then
    FirstPathLineNo := StrToInt(PathLineNos.Strings[0])
  else
    FirstPathLineNo := 0;

  // Modify path
  ModifyPathString(OldPath, DirName, Method, True, ResultPath);

  // Write Modified path back to Autoexec.bat
  // First delete all existing path references from Autoexec.bat
  Index := PathLineNos.Count-1;
  while (Index>=0) do
  begin
    AutoexecLines.Delete(StrToInt(PathLineNos.Strings[Index]));
    Index := Index-1;
  end;
  // Then insert new path variable into Autoexec.bat
  AutoexecLines.Insert(FirstPathLineNo, '@PATH='+ResultPath);
  // Delete old Autoexec.bat from disk
  if not DeleteFile('c:\Autoexec.bat') then
    result := mpAutoexecNoWriteAcc;
  Sleep(500);
  // And finally write Autoexec.bat back to disk
  if not (result=mpAutoexecNoWriteAcc) then
    SaveStringToFile('c:\Autoexec.bat', AutoexecLines.Text, false);

  // Free stringlists
  PathLineNos.Free;
  AutoexecLines.Free;
end; // ModifyPath9x

{ Helper function: Modify path on Windows NT, 2000 and XP }
function ModifyPathNT(DirName: string; Method, Scope: integer): integer;
var
  RegRootKey: integer;
  RegSubKeyName: string;
  RegValueName: string;
  OldPath, ResultPath: string;
  OK: boolean;
begin
  // Expect everything to be OK
  result := mpOK;

  // Initialize registry key and value names to reflect if changes should be global or local to current user only
  case Scope of
    psCurrentUser:
      begin
        RegRootKey := HKEY_CURRENT_USER;
        RegSubKeyName := 'Environment';
        RegValueName := 'Path';
      end;
    psAllUsers:
      begin
        RegRootKey := HKEY_LOCAL_MACHINE;
        RegSubKeyName := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
        RegValueName := 'Path';
      end;
  end;

  // Read current path value from registry
  OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, OldPath);
  if not OK then
  begin
    result := mpMissingRights;
    Exit;
  end;

  // Modify path
  ModifyPathString(OldPath, DirName, Method, False, ResultPath);

  // Write new path value to registry
  if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultPath) then
  begin
    result := mpMissingRights;
    Exit;

  end;
end; // ModifyPathNT

{ Main function: Modify path }
function ModifyPath(Path: string; Method, Scope: integer): integer;
begin
  // Check if both add and remove has been specified (= error!)
  if (Method and (pmAddToBeginning or pmAddToEnd) and (pmRemove or pmRemoveSubdirsAlso)) > 0 then
  begin
    result := mpBothAddAndRemove;
    Exit;
  end;

  // Perform directory constant expansion
  Path := ExpandConstantEx(Path, ' ', ' ');

  // Test if Win9x
  if InstallOnThisVersion('4,0','0,0') = irInstall then
    ModifyPath9x(Path, Method);

  // Test if WinNT, 2000 or XP
  if InstallOnThisVersion('0,4','0,0') = irInstall then
    ModifyPathNT(Path, Method, Scope);
end; // ModifyPath

//* Called on setup startup *//
function InitializeSetup(): Boolean;
var
  javaPath: String;
begin
  Result := true;
  javaPath := getJavaPath(getJavaVersion());
  if Length( getJavaVersion() ) = 0 then begin
    //* No Java detected *//
    MsgBox('Setup detected, that there is no Java Runtime installed.' + #13 +
            'You must have installed Java Runtime ' + MIN_JAVA_VERSION + ' or higher to continue setup.' + #13 +
            'Please download and install from http://java.sun.com and restart setup.', mbInformation, MB_OK);
    Result := false;
  end
  else begin
    //* Java version lower than MIN_JAVA_VERSION detected. Older versions *//
    if (getJavaVersion()) < MIN_JAVA_VERSION then begin
      MsgBox('You must upgrade Java Runtime to version ' + MIN_JAVA_VERSION + ' or higher.' + #13 +
             'Please download and install from http://java.sun.com and restart setup.', mbInformation, MB_OK);
      Result := false
    end
  end;
  if(Result) then begin //*Add Java to beginning of PATH, if not already there*//
//*Note that this does not check nor expand %JAVA_HOME% causing possible duplicate*//
//*We should offer regular users the option to log in as Admin, instead*//
    if MsgBox('Java required in PATH.' + #13 + 'OK to add latest Java to PATH?' + #13 +
     '(Choose yes, if unsure)', mbConfirmation, MB_YESNO) = IDYES then
    begin //*user clicked Yes*//
      if (IsAdminLoggedOn()) then begin
        ModifyPath(javaPath, pmAddToBeginning, psAllUsers);
      end else begin
        ModifyPath(javaPath, pmAddToBeginning, psCurrentUser);
      end
      //*Const WM_SETTINGCHANGE = 16+10; //&H1A*//
      SendBroadcastNotifyMessage(16+10, 0, 0); //*should be added to ModifyPath()*//
    end  //*User clicked No: do nothing; assume they know better*//
  end

//* Called when removing program: function usUninstall(); *//

end;


86 - How to create an upgrade

Article created: 18/04/05 17:27:55
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=86

See HOWTO: Create an installation that is an "update" or "add-on" to an existing installation

What I want more or different is:
  • check version of program installed
  • backup some files
  • skip destination folder page
  • update Add/Remove Programs list with the new version installed

; Inno Setup Compiler 5.0.8
; ISPP 5.0.6.0

;retrieve version of the program from exe
#define AppVersion GetFileVersion(AddBackslash(SourcePath) + "MyProg.exe")

[Setup]
AppName=Upgrade
AppID=Upgrade
;use the version in AppVerName
AppVerName=Upgrade {#AppVersion}
;output file name include the version
OutputBaseFilename=Ugrade_{#AppVersion}
;the setup exe has same version
VersionInfoVersion={#AppVersion}
VersionInfoDescription=Upgrade {#AppVersion}
; set the default path where the application was previusly installed GetPrevDir
DefaultDirName={code:GetPrevDir|{pf}\Upgrade}
DisableProgramGroupPage=true
DisableStartupPrompt=true

[Files]
;Backup MyProg.exe
Source: {app}\MyProg.exe; DestDir: {code:GetBckdir|''}; Flags: external skipifsourcedoesntexist
; Install MyProg.exe
Source: MyProg.exe; DestDir: {app}

[Code]
procedure DecodeVersion( verstr: String; var verint: array of Integer );
var
  i,p: Integer; s: string;
begin
  // initialize array
  verint := [0,0,0,0];
  i := 0;
  while ( (Length(verstr) > 0) and (i < 4) ) do
  begin
      p := pos('.', verstr);
      if p > 0 then
      begin
      if p = 1 then s:= '0' else s:= Copy( verstr, 1, p - 1 );
        verint[i] := StrToInt(s);
        i := i + 1;
        verstr := Copy( verstr, p+1, Length(verstr));
      end
      else
      begin
        verint[i] := StrToInt( verstr );
        verstr := '';
      end;
  end;

end;

// This function compares version string
// return -1 if ver1 < ver2
// return  0 if ver1 = ver2
// return  1 if ver1 > ver2
function CompareVersion( ver1, ver2: String ) : Integer;
var
  verint1, verint2: array of Integer;
  i: integer;
begin

  SetArrayLength( verint1, 4 );
  DecodeVersion( ver1, verint1 );

  SetArrayLength( verint2, 4 );
  DecodeVersion( ver2, verint2 );

  Result := 0; i := 0;
  while ( (Result = 0) and ( i < 4 ) ) do
  begin
      if verint1[i] > verint2[i] then
        Result := 1
      else
      if verint1[i] < verint2[i] then
          Result := -1
        else
          Result := 0;

      i := i + 1;
  end;

end;

// Get where the application was installed
function GetPathInstalled( AppID: String ): String;
var
   sPrevPath: String;
begin
  sPrevPath := '';
  if not RegQueryStringValue( HKLM,
    'Software\Microsoft\Windows\CurrentVersion\Uninstall'+AppID+'_is1',
    'Inno Setup: App Path', sPrevpath) then
    RegQueryStringValue( HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall'+AppID+'_is1' ,
      'Inno Setup: App Path', sPrevpath);

  Result := sPrevPath;
end;

const THIS_VERSION = '{#AppVersion}';

var
  // previuos install dir
  PrevDir: String;
  // tell if it's an upgrade
  Upgrade: Boolean;
  // installed program version
  InstalledVer: String;
  // backupdir
  BckDir: String;

function InitializeSetup(): Boolean;

var
    vercomp, answ : Integer;
begin

  Result := true;

  // read the installtion folder
  PrevDir := GetPathInstalled( '{#SetupSetting("AppID")}' );

  if length( Prevdir ) > 0 then begin

    // I found the folder so
      // It's an upgrade
      Upgrade := true;

      // read the version of MyProg.exe already installed
      GetVersionNumbersString(  PrevDir + '\MyProg.exe' , InstalledVer );

      // compare versions
      vercomp := CompareVersion( InstalledVer, THIS_VERSION );

    // version smaller
    if vercomp < 0 then
    begin
          // keep Result as true and go on with upgrade
    end;

    // same version ask what to do
    if vercomp = 0 then
    begin

        answ := MsgBox( 'Found same version of {#SetupSetting("AppName")} ('+ InstalledVer  +').'
              + #13#13 + 'Do you want to upgrade anyway ?',
              mbConfirmation, MB_YESNO )

        // If user answer is NO abort setup
        Result := ( answ = IDYES );
    end;

    // version bigger, cannot upgrade with a smaller version
    if vercomp > 0 then
    begin
            MsgBox('Found '+ InstalledVer +' version of {#SetupSetting("AppName")} '+
                ' and you want to install ' + THIS_VERSION
                + #13#13 + 'Setup/Upgrade aborted.', mbError, MB_OK );
        Result := false;
    end;

  end
  else begin
        MsgBox( 'No installation found of MyProg.exe, upgrade aborted.',
          mbInformation, MB_OK );
        // abort setup
      Result := false;
  end;

  // backup dir has the program version found
  BckDir := Prevdir + '\Backup' + InstalledVer;

end;

function GetPrevDir( s: String ) : String;
begin
    if length( PrevDir ) = 0 then
        Result := s
    else
        Result := PrevDir;
end;

function GetBckDir( s: String): String;
begin
  ForceDirectories( BckDir );
  Result := BckDir;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  // skip selectdir if It's an upgrade
  if ( PageID = wpSelectDir )  and Upgrade then
    Result := true
  else
    Result := false;
end;


87 - How to test a SQL Server connection with ADO

Article created: 09/05/05 16:12:52
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=87

It is possible, using Automation, create an ADODB.Connection object to test a connection to a SQL Server/MSDE with Server Name, User Name and Password.

See also:

How to test a SQL Server connection using ODBC driver
TestLoginADO.iss

; Inno Setup 5.0.8

[Setup]
AppName=TestLoginADO
AppVerName=TestLoginADO
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\TestLoginADO
DisableStartupPrompt=true
CreateAppDir=false
OutputBaseFilename=TestLoginADO
AllowUNCPath=false

[Code]

function TestADOConnection( server, username, password: String ) : Boolean;
var
  adocon: Variant;
  adostr: String;
begin

  Result := false;

   adostr := 'Provider=SQLOLEDB.1;Persist Security Info=False;' +
    'User ID='+ username +';Data Source=' + server +
    ';Password=' + password;

  try
    adocon := CreateOleObject('ADODB.Connection');
  except
    RaiseException('Please install ADO (MDAC) first.'#13#13+
          '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

  try
    adocon.Open( adostr );
  except
    RaiseException('Error Connecting.'#13#13'(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

  Result := true;

end;

#include "TestLoginADO.isd"

procedure InitializeWizard();
begin
    LoginSQL_CreatePage(wpWelcome);
end;
TestLoginADO.isd

  [CustomMessages]
  LoginSQLCaption=LoginSQL Caption
  LoginSQLDescription=LoginSQL description

  [Code]
  var
    lbUsername: TLabel;
    lbServer: TLabel;
    lbPassword: TLabel;
    edUsername: TEdit;
    edServer: TEdit;
    edPassword: TEdit;

  procedure LoginSQL_Activate(Page: TWizardPage);
  begin
  end;

  function LoginSQL_ShouldSkipPage(Page: TWizardPage): Boolean;
  begin
    Result := False;
  end;

  function LoginSQL_BackButtonClick(Page: TWizardPage): Boolean;
  begin
    Result := True;
  end;

  function LoginSQL_NextButtonClick(Page: TWizardPage): Boolean;
  begin
    Result := TestADOConnection( edServer.Text, edUsername.Text, edPassword.Text  );
  end;

  procedure LoginSQL_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean);
  begin
  end;

  function LoginSQL_CreatePage(PreviousPageId: Integer): Integer;
  var
    Page: TWizardPage;
  begin
    Page := CreateCustomPage(
      PreviousPageId,
      ExpandConstant('{cm:LoginSQLCaption}'),
      ExpandConstant('{cm:LoginSQLDescription}')
    );

    { lbUsername }
    lbUsername := TLabel.Create(Page);
    with lbUsername do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(80);
      Width := ScaleX(48);
      Height := ScaleY(13);
      Caption := 'Username';
      FocusControl := edUsername;
    end;

    { lbServer }
    lbServer := TLabel.Create(Page);
    with lbServer do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(16);
      Width := ScaleX(32);
      Height := ScaleY(13);
      Caption := 'Server';
      FocusControl := edServer;
    end;

    { lbPassword }
    lbPassword := TLabel.Create(Page);
    with lbPassword do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(144);
      Width := ScaleX(46);
      Height := ScaleY(13);
      Caption := 'Password';
      FocusControl := edPassword;
    end;

    { edUsername }
    edUsername := TEdit.Create(Page);
    with edUsername do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(96);
      Width := ScaleX(325);
      Height := ScaleY(21);
      AutoSize := False;
      TabOrder := 1;
      Text := 'sa';
    end;

    { edServer }
    edServer := TEdit.Create(Page);
    with edServer do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(32);
      Width := ScaleX(329);
      Height := ScaleY(21);
      AutoSize := False;
      TabOrder := 0;
      Text := 'localhost';
    end;

    { edPassword }
    edPassword := TEdit.Create(Page);
    with edPassword do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(160);
      Width := ScaleX(325);
      Height := ScaleY(21);
      AutoSize := False;
      PasswordChar := '*';
      TabOrder := 2;
    end;

    with Page do
    begin
      OnActivate := @LoginSQL_Activate;
      OnShouldSkipPage := @LoginSQL_ShouldSkipPage;
      OnBackButtonClick := @LoginSQL_BackButtonClick;
      OnNextButtonClick := @LoginSQL_NextButtonClick;
      OnCancelButtonClick := @LoginSQL_CancelButtonClick;
    end;

    Result := Page.ID;
  end;


88 - How to create and modify an Access MDB with ADO

Article created: 10/05/05 13:02:03
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=88

Using object ADOX.Catalog it is possible to create an MDB file, and using object ADODB.Connection it is possible to excute SQL statements on a open connection, in this sample, I create a table and populate it with some info of the installer.

In this script I do not check for any MDAC or Jet presence.

See also:

How to detect version of MDAC installed
How to detect if JET engine is installed

; made with Inno Setup 5.0.8

[Setup]
AppName=SQL2MDB
AppVerName=SQL2MDB
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}\SQL2MDB
DisableStartupPrompt=true
CreateAppDir=false
OutputBaseFilename=SQL2MDB

[Code]
var
  // page for the progress bar
  ProgressPage: TOutputProgressWizardPage;
  // ADO object
  adocon: Variant;

//
// create a new database using ADOX
//
procedure CreateNewMDB(FileName: String );
var
  Catalog: Variant;
begin

  // creato ADOX object
  try
    Catalog := CreateOleObject('ADOX.Catalog');
  except
    RaiseException('Please install ADO (MDAC) first.'#13#13 +
      '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

  // if the file already exists ask if user want to delete it
  if FileExists( FileName ) then
    if MsgBox( 'File '+FileName+' already exits, do you want to delete it ?',
      mbConfirmation, MB_YESNO ) = IDYES then
        DeleteFile( FileName );

  // create the file
  Catalog.Create( 'Provider=Microsoft.Jet.OLEDB.4.0;' +
    'Jet OLEDB:Engine Type=5;Data Source=' + FileName) ;
end;

//
// esecute a SQL statement on given ado connection
//
function ExecuteSQL( sql: String ) : Boolean;
begin

  Result := false;
  try
    adocon.Execute( sql );
    Result := true;
  except
    RaiseException('Error excuting sql->'+sql+#13#13 +
      '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

end;

//
// Add a message to the table
//
function AddMsg( msg: String ) : Boolean;
var sql: String;
begin

  sql := 'INSERT INTO MyTable ( MyType, Message ) VALUES ( '+
    IntToStr(Random(100))+', "'+msg+'" )';
  Result := ExecuteSQL( sql );
end;

// Create and connect the ADO object to DB
function Connect( FileName: String ) : Boolean;
var
  adostr: String;
begin

  Result := false;
  // build the ADO connection string
  adostr := 'Provider=Microsoft.Jet.OLEDB.4.0;Persist Security Info=False;' +
    'Data Source='+FileName;

  // create the object
  try
    adocon := CreateOleObject('ADODB.Connection');
  except
    RaiseException('Please install ADO (MDAC) first.'#13#13+
      '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

  // open the connection
  try
    adocon.Open( adostr );
  except
    RaiseException('Error Connecting.'#13#13 +
      '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;
  Result := true;
end;

procedure InitializeWizard();
begin
  // Create the pages
  ProgressPage := CreateOutputProgressPage(
    'Database modification',
    'Executing operation on db...');

end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  I: Integer;
  filename : String;
begin
  if CurPageID = wpWelcome then begin

    filename := 'C:\Sql2MDB.mdb';
    ProgressPage.SetProgress(0, 0);
    ProgressPage.Show;
    try
      for I := 1 to 3 do begin

        ProgressPage.SetProgress(I, 3);

        // create the database
        if I = 1 then  begin
          ProgressPage.SetText('Creating Database...', '');
          CreateNewMDB( filename );
        end;

        // create the table
        if I = 2 then  begin
          ProgressPage.SetText('Creating table...', '');
          If Connect(  filename) then
            ExecuteSQL(  'CREATE TABLE MyTable ( MyType int, Message varchar(50) ) ');
        end;

        // populate table with some records
        if I = 3 then  begin
          ProgressPage.SetText('Creating Records...', '');
          AddMsg( ExpandConstant('{src}'));
          AddMsg( ExpandConstant('{srcexe}'));
          AddMsg( ExpandConstant('{pf}'));
          AddMsg( ExpandConstant('{cf}'));
          AddMsg( ActiveLanguage );
          AddMsg( GetWindowsVersionString );
        end;

        Sleep(500);
      end;

    finally
      ProgressPage.Hide;
    end;

    // close the connection
    adocon.Close;

  end;

  Result := true;
end;


89 - Validating Windows Username and Password

Author: aravindtj
Article created: 09/06/05 9:48:55
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=89

It is a common problem in Windows user authentication validation while installing Windows Service or for some other applications. For this purpose, we have to validate the Windows authentication details for that user. For Windows NT based machines, there are chances to fail when using LogOnUser method. So, an easy method is use of NetAPI32.dll functions.

Using that API, NetUserChangePassword method can be used for validating the username and password. The syntax of NetUserChangePassword is:
NetUserChangePassword(domain_name, user_name, old_pass_word, new_pass_word);

By giving old and new password as same value, it will validate the username and password. This function is not available in Windows 98, Me, 95 machines. For those OSes, PwdChangePassword method is recommended by MS. The details are available at http://support.microsoft.com/kb/q177200/.

To avoid error message when loading NetAPI32.dll functions in Windows98 etc machines, give delayload option when extracting functions from the dll file.

Thanks for Inno News group for their support.
The code examples are as follows:

function NetUserChangePassword(servername, username, oldPassword, newPassword: PChar): Integer;
        external 'NetUserChangePassword@NETAPI32.DLL stdcall delayload';

function MultiByteToWideChar(CodePage, Flags: Integer; aStr: String; aLen: Integer; uStr: String; uLen: Integer): Integer;
        external 'MultiByteToWideChar@KERNEL32.DLL stdcall';

function ValidateWindowsUser(userName, passWord: string) : Boolean;
var
    serverName, server_name, user_name, pass_word : String;
    status : LongInt;
begin
    SetLength(server_name, 256);
    SetLength(user_name, 256);
    SetLength(pass_word, 256);

    serverName := '' + ExpandConstant('{computername}');

    MultiByteToWideChar(0, 0, serverName, -1, server_name, 256);
    MultiByteToWideChar(0, 0, userName, -1, user_name, 256);
    MultiByteToWideChar(0, 0, passWord, -1, pass_word, 256);

    status := NetUserChangePassword(server_name, user_name, pass_word, pass_word);

    if(status = 0) then
        Result := True
    else
        Result := False
end;

This validation method is tested on Windows 2000, XP and 2003 server.
Inno version is 5.0.8.


90 - How to ask for a drive to install for IS 5

Article created: 15/07/05 10:07:32
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=90

If the folder selection page it''s added a combobox and are changed some properties in the exiting controls it''s possible to give to an user the way to choose only a drive where to install the program.
In this way in the script it''s possible to use the {app} constant instead of a more complex {code:}.
In this sample removable drive are not inserted in the list, with small code changes it''s possible for example exclude also CD-ROM drives.

[Setup]
AppName=My Program
AppVerName=My Program version 1.5
DefaultDirName={sd}\My Program
DefaultGroupName=My Program
UninstallDisplayIcon={app}\MyProg.exe
Compression=lzma
SolidCompression=true
OutputDir=.
DisableProgramGroupPage=true
UsePreviousGroup=false
ShowLanguageDialog=yes

[Files]
Source: MyProg.exe; DestDir: {app}

[Messages]
SelectDirBrowseLabel=To continue, click Next. If you would like to select a different drive, choose from the list.

[Code]

var
    // combo box for drives
    cbDrive : TComboBox ;
    // array fo string that keep the drive letters
    DrvLetters: array of String;

function GetDriveType( lpDisk: String ): Integer;
external 'GetDriveTypeA@kernel32.dll stdcall';

function GetLogicalDriveStrings( nLenDrives: LongInt; lpDrives: String ): Integer;
external 'GetLogicalDriveStringsA@kernel32.dll stdcall';

const
  DRIVE_UNKNOWN = 0; // The drive type cannot be determined.
  DRIVE_NO_ROOT_DIR = 1; // The root path is invalid. For example, no volume is mounted at the path.
  DRIVE_REMOVABLE = 2; // The disk can be removed from the drive.
  DRIVE_FIXED = 3; // The disk cannot be removed from the drive.
  DRIVE_REMOTE = 4; // The drive is a remote (network) drive.
  DRIVE_CDROM = 5; // The drive is a CD-ROM drive.
  DRIVE_RAMDISK = 6; // The drive is a RAM disk.

// function to convert disk type to string
function DriveTypeString( dtype: Integer ): String ;
begin
  case dtype of
    DRIVE_NO_ROOT_DIR : Result := 'Root path invalid';
    DRIVE_REMOVABLE : Result := 'Removable';
    DRIVE_FIXED : Result := 'Fixed';
    DRIVE_REMOTE : Result := 'Network';
    DRIVE_CDROM : Result := 'CD-ROM';
    DRIVE_RAMDISK : Result := 'Ram disk';
  else
    Result := 'Unknown';
  end;
end;

// change folder accordigly to the drive letter selected
procedure cbDriveOnClick(Sender: TObject);
begin
    WizardForm.DirEdit.Text := DrvLetters[ cbDrive.ItemIndex ] + 'My program';
end;

procedure FillCombo();
var
  n: Integer;
  drivesletters: String; lenletters: Integer;
  drive: String;
  disktype, posnull: Integer;

begin

  //get all drives letters of system
  drivesletters := StringOfChar( ' ', 64 );
  lenletters := GetLogicalDriveStrings( 63, drivesletters );
  SetLength( drivesletters , lenletters );

  drive := '';
  n := 0;
  while ( (Length(drivesletters) > 0) ) do
  begin
    posnull := Pos( #0, drivesletters );
      if posnull > 0 then
      begin
      drive:= UpperCase( Copy( drivesletters, 1, posnull - 1 ) );

      // get number type of disk
      disktype := GetDriveType( drive );

      // add it only if it is not a floppy
      if ( not ( disktype = DRIVE_REMOVABLE ) ) then
      begin

            cbDrive.Items.Add( drive + '   [' + DriveTypeString( disktype ) + ']' )

        SetArrayLength(DrvLetters, N+1);
        DrvLetters[n] := drive;

        // default select C: Drive
        if ( drive = 'C:' ) then cbDrive.ItemIndex := n;

        n := n + 1;
      end

        drivesletters := Copy( drivesletters, posnull+1, Length(drivesletters));
      end
  end;

  cbDriveOnClick( cbDrive );

end;

procedure InitializeWizard();
begin

    // create the combo box for drives
    cbDrive:= TComboBox.Create(WizardForm.SelectDirPage);
    with cbDrive do
    begin
      Parent := WizardForm.DirEdit.Parent;

      Left := WizardForm.DirEdit.Left;
      Top := WizardForm.DirEdit.Top + WizardForm.DirEdit.Height * 2;
      Width := WizardForm.DirEdit.Width;

      Style := csDropDownList;
    end;

    // hide the Browse button
    WizardForm.DirBrowseButton.Visible := false;

    // Edit box for folder don't have to be editable
    WizardForm.DirEdit.Enabled := false;

    // fill combo box with Drives
    FillCombo;

    // set the event on combo change
    cbDrive.OnClick := @cbDriveOnClick ;
end;


91 - How to prompt for an additional folder for data for IS 5

Article created: 07/08/05 15:31:09
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=91

This script shows how to insert a custom wizard page after the directory page, and prompt for another folder where to install some other files, for example a database.
This script is a small version of a more complex script that shows also many things about custom wizard pages, that script is ScriptDlg.iss and is in Examples folder of your Inno Setup installation folder.

;
; Inno Setup 5.0.8
;

[Setup]
AppName=DataDir
AppVerName=DataDir V5
DefaultDirName={pf}\DataDir
DisableProgramGroupPage=yes
UninstallDisplayIcon={app}\MyProg.exe

[Files]
;Main program that will be installed in {app} folder
Source: MyProg.exe; DestDir: {app}

;Database file that will installed where user choosed
Source: DataBase.mdb; DestDir: {code:GetDataDir}

[Code]
var
  DataDirPage: TInputDirWizardPage;

procedure InitializeWizard;
begin
  // Create the page

  DataDirPage := CreateInputDirPage(wpSelectDir,
    'Select Personal Data Directory', 'Where should personal data files be installed?',
    'Select the folder in which Setup should install personal data files, then click Next.',
    False, '');
  DataDirPage.Add('');

  DataDirPage.Values[0] := GetPreviousData('DataDir', '');
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
  // store the folder chosed, for a further reinstall/upgrade
  SetPreviousData(PreviousDataKey, 'DataDir', DataDirPage.Values[0]);
end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
  // set default folder if empty
  if DataDirPage.Values[0] = '' then
     DataDirPage.Values[0] := ExpandConstant('{sd}\DataDir');
  Result := True;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  // Fill the 'Ready Memo' with the normal settings and the custom settings
  S := '';

  S := S + MemoDirInfo + NewLine + NewLine;

  S := S + 'Database path' + NewLine;
  S := S + Space + DataDirPage.Values[0] + NewLine;

  Result := S;
end;

function GetDataDir(Param: String): String;
begin
  { Return the selected DataDir }
  Result := DataDirPage.Values[0];
end;


92 - How to call psvince.dll on install and uninstall

Article created: 04/09/05 20:14:34
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=92

psvince.dll is a dll that export a function to detect if a module is loaded in memory. Read article PSVince is a DLL to detect if a module is loaded in memory.
To compile the following sample you need to download psvince.dll.
The following code check if notepad is running, if notepad is running it is not possible to install the program, same check is performed at uninstall time, so if you want to unistall it notepad have not to be running.

;
; IS 5.0.8
;

[Setup]
AppName=UninsPSVince
AppVerName=UninsPSVince
DisableProgramGroupPage=true
DisableStartupPrompt=true
DefaultDirName={pf}\UninsPSVince

[Files]
; dll used to check running notepad at install time
Source: psvince.dll; flags: dontcopy
;psvince is installed in {app} folder, so it will be
;loaded at uninstall time ;to check if notepad is running
Source: psvince.dll; DestDir: {app}

[Code]
// function IsModuleLoaded to call at install time
// added also setuponly flag
function IsModuleLoaded(modulename: String ):  Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall setuponly';

// function IsModuleLoadedU to call at uninstall time
// added also uninstallonly flag
function IsModuleLoadedU(modulename: String ):  Boolean;
external 'IsModuleLoaded@{app}\psvince.dll stdcall uninstallonly' ;

function InitializeSetup(): Boolean;
begin

  // check if notepad is running
  if IsModuleLoaded( 'notepad.exe' ) then
  begin
    MsgBox( 'Notepad is running, please close it and run again setup.',
             mbError, MB_OK );
    Result := false;
  end
  else Result := true;
end;

function InitializeUninstall(): Boolean;
begin

  // check if notepad is running
  if IsModuleLoadedU( 'notepad.exe' ) then
  begin
    MsgBox( 'Notepad is running, please close it and run again uninstall.',
             mbError, MB_OK );
    Result := false;
  end
  else Result := true;

  // Unload the DLL, otherwise the dll psvince is not deleted
  UnloadDLL(ExpandConstant('{app}\psvince.dll'));

end;


93 - How to browse for a machine/host

Article created: 06/09/05 22:30:53
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=93

Author: Oliver Bock
Code from original post on http://news.jrsoftware.org/news/innosetup.code/msg09691.html

;
; Inno Setup 5.0.8
;
; original code of Oliver Bock
; http://news.jrsoftware.org/news/innosetup.code/msg09691.html

[Setup]
AppName=BrowseMachine
AppVerName=BrowseMachine
DefaultDirName={pf}\BrowseMachine
DisableProgramGroupPage=yes
CreateAppDir=false
Uninstallable=false

[Code]
type
   PAnsiChar = PChar;
   LPARAM = DWORD;
   HRESULT = Longint;
   PIDL = DWORD;  // Opaque pointer.

   BROWSEINFO = record
     hwndOwner: HWND;
     pidlRoot: PIDL;
     pszDisplayName: PAnsiChar;
     lpszTitle: PAnsiChar;
     ulFlags: UINT;
     lpfn: DWORD;
     lParam: LPARAM;
     iImage: Integer;
   end;

function SHGetSpecialFolderLocation(hwndOwner: HWND; nFolder: Integer; var pidl: DWORD): HRESULT;
external 'SHGetSpecialFolderLocation@shell32.dll stdcall';

function SHBrowseForFolder(var lpbi: BROWSEINFO): PIDL;
external 'SHBrowseForFolder@shell32.dll stdcall';

function SHGetPathFromIDList(aPidl: PIDL; path: PChar): Boolean;
external 'SHGetPathFromIDList@shell32.dll stdcall';

const
    BIF_BROWSEFORCOMPUTER = $1000;
    CSIDL_NETWORK = $12;

var
    ServerPage: TInputQueryWizardPage;

procedure BrowseComputer(Sender: TObject);
var
    display_name, title: string;
    bi: BROWSEINFO;
    network, server_pidl: DWORD;
    hwndParent: HWND;
    NullPos : Integer;
begin
    // Work out where My Network Places is.
    hwndParent := WizardForm.Handle;
    SHGetSpecialFolderLocation(hwndParent, CSIDL_NETWORK, network);

    // Invoke the Browse dialog.
    bi.hwndOwner := hwndParent;
    bi.pidlRoot := network;
    SetLength(display_name, 261);
    bi.pszDisplayName := PChar(display_name);
    title := 'Select the Server:';
    bi.lpszTitle := PChar(title);
    bi.ulFlags := BIF_BROWSEFORCOMPUTER;
    bi.lpfn := 0;
    bi.lParam := 0;
    server_pidl := SHBrowseForFolder(bi);
    if server_pidl <> 0 then begin
       // Extract the computer name.

       NullPos := Pos(#0, display_name);
       if NullPos > 0 then SetLength(display_name, NullPos - 1);

       ServerPage.Values[0] := display_name;
    end;
end;

procedure InitializeWizard;
var
  button : TButton;
begin
  // Create the page

  // If installing as client, make the machine name a simple edit
  // box and add a browse button.
  ServerPage := CreateInputQueryPage(
          wpWelcome,
          'Select Server',
          'What machine is your server on?',
          'You must tell us which machine you will use as a server so that the program can connect automatically when it is started.');
    ServerPage.Add('Machine name:', False);
    button := TButton.Create(WizardForm);
    button.Parent := ServerPage.Surface;
    button.Left := WizardForm.DirBrowseButton.Left;
    button.Top := ServerPage.Edits[0].Top - (WizardForm.DIREDIT.Top - WizardForm.DirBrowseButton.Top);
    button.Width := WizardForm.DirBrowseButton.Width;
    button.Height := WizardForm.DirBrowseButton.Height;
    button.Caption := WizardForm.DirBrowseButton.Caption;
    button.OnClick := @BrowseComputer;
    ServerPage.Edits[0].Width := WizardForm.DIREDIT.Width;
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  // Fill the 'Ready Memo' with the normal settings and the custom settings
  S := '';
  S := S + 'Server Machine:' + NewLine;
  S := S + Space + ServerPage.Values[0] + NewLine;

  Result := S;
end;


94 - How to include a second license page

Author: eagle00789
Article created: 07/09/05 15:34:57
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=94

Just copy the following code to your script and replace the line Lines.Add('Memo1'); with as many lines you need for the 2nd license-page

[Code]
var
  Label1: TLabel;
  Memo1: TMemo;
  RadioButton1: TRadioButton;
  RadioButton2: TRadioButton;

procedure CustomForm_Activate(Page: TWizardPage);
begin
  if RadioButton1.Checked = True then
    WizardForm.NextButton.Enabled := True
  else
    WizardForm.NextButton.Enabled := False;
end;

procedure Radio1_OnClick(Sender: TObject);
begin
  WizardForm.NextButton.Enabled := True;
end;

procedure Radio2_OnClick(Sender: TObject);
begin
  WizardForm.NextButton.Enabled := False;
end;

function CustomForm_ShouldSkipPage(Page: TWizardPage): Boolean;
begin
  Result := False;
end;

function CustomForm_BackButtonClick(Page: TWizardPage): Boolean;
begin
  Result := True;
end;

function CustomForm_NextButtonClick(Page: TWizardPage): Boolean;
begin
  Result := True;
end;

procedure CustomForm_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean);
begin
end;

function CustomForm_CreatePage(PreviousPageId: Integer): Integer;
var
  Page: TWizardPage;
begin
  Page := CreateCustomPage(
    PreviousPageId,
    SetupMessage(msgWizardLicense),
    SetupMessage(msgLicenseLabel)
  );
  { Label1 }
  Label1 := TLabel.Create(Page);
  with Label1 do
  begin
    Parent := Page.Surface;
    WordWrap := True;
    Left := ScaleX(0);
    Top := ScaleY(0);
    Width := ScaleX(414);
    Height := ScaleY(26);
    Caption := SetupMessage(msgLicenseLabel3);
  end;
  { Memo1 }
  Memo1 := TMemo.Create(Page);
  with Memo1 do
  begin
    Parent := Page.Surface;
    Left := ScaleX(0);
    Top := ScaleY(40);
    Width := ScaleX(413);
    Height := ScaleY(145);
    Lines.Add('Memo1');
    ReadOnly := True;
    TabOrder := 0;
  end;
  { RadioButton1 }
  RadioButton1 := TRadioButton.Create(Page);
  with RadioButton1 do
  begin
    Parent := Page.Surface;
    Left := ScaleX(0);
    Top := ScaleY(196);
    Width := ScaleX(169);
    Height := ScaleY(17);
    Caption := SetupMessage(msgLicenseAccepted);
    TabOrder := 1;
    OnClick := @Radio1_OnClick;
  end;
  { RadioButton2 }
  RadioButton2 := TRadioButton.Create(Page);
  with RadioButton2 do
  begin
    Parent := Page.Surface;
    Left := ScaleX(0);
    Top := ScaleY(216);
    Width := ScaleX(169);
    Height := ScaleY(17);
    Caption := SetupMessage(msgLicenseNotAccepted);
    Checked := True;
    TabOrder := 2;
    TabStop := True;
    OnClick := @Radio2_OnClick;
  end;
  with Page do
  begin
    OnActivate := @CustomForm_Activate;
    OnShouldSkipPage := @CustomForm_ShouldSkipPage;
    OnBackButtonClick := @CustomForm_BackButtonClick;
    OnNextButtonClick := @CustomForm_NextButtonClick;
    OnCancelButtonClick := @CustomForm_CancelButtonClick;
  end;
  Result := Page.ID;
end;

procedure InitializeWizard();
begin
  CustomForm_CreatePage(wpLicense);
end;


95 - Set Environment variables in Windows 9x, NT, 2000 or XP

Author: axis23
Article created: 15/10/05 15:40:42
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=95

I adapted Native ISX procedures for PATH modification from Thomas Vedel (ModifyPath.iss), and I created one script to add/modify environment variables in any windows version.
Function: ModifyVar(VarName,VarValue,Method,Scope)

VarName: Name of modified variable
VarValue: Value of modified variable
Method: vmAddOnlyIfVarDoesNotExists (Only modify if variable doesn''t exists)
             vmAllways (Modify Always)
Scope:  vsCurrentUser (Only for current user)
            vsAllUsers (For all users)

[Code]

// Version log:
// 09/22/2005: Initial release (axis23(at)gmail.com)
// Based in 'Native ISX procedures for PATH modification' from Thomas Vedel ('ModifyPath.iss')

const
// Modification method
vmAddOnlyIfVarDoesNotExists = $1; // Add Var only is this Var doesn't exists
vmAddAllways = $2; // Add Var allways

// Scope
vsCurrentUser = 1; // Add Var for current user
vsAllUsers = 2; // Add Var for all users

// Error results
mvOK = 0; // No errors
mvMissingRights = -1; // User has insufficient rights
mvAutoexecNoWriteacc = -2; // Autoexec can not be written (may be readonly)
mvBothMethods = -3; // Error if invoque function with two methods

{ Helper function: Modify Var on Windows 9x }
function ModifyVar9x(VarName, VarValue: string; Method: integer): integer;
var
AutoexecLines: TStringList;
ActualLine: String;
VarLineNos: TStringList;
FirstVarLineNo: Integer;
VarExists: boolean;
LineNo, CharNo, Index: integer;

TempString: String;
TempVarName: String;
TempVarName_comp: String;

begin
// Expect everything to be OK
result := mvOK;

// Create stringslists
AutoexecLines := TStringList.Create;
VarLineNos := TStringList.Create;
TempVarName := 'SET ' + uppercase(VarName) + '=';
TempVarName_comp := 'SET' + uppercase(VarName) + '=';

// Create VarExists
VarExists := false;

// Read existing Var
LoadStringFromFile('c:Autoexec.bat', TempString);
AutoexecLines.Text := TempString;
VarLineNos.Clear;
// Read Autoexec line by line
for LineNo := 0 to AutoexecLines.Count - 1 do begin
ActualLine := AutoexecLines.Strings[LineNo];
// Check if line starts with 'Varname=' after first stripping spaces and other "fill-chars"
if Pos('=', ActualLine) > 0 then
begin
for CharNo := Pos('=', ActualLine)-1 downto 1 do
if (ActualLine[CharNo]=' ') or (ActualLine[CharNo]=#9) then
Delete(ActualLine, CharNo, 1);
if Pos('@', ActualLine) = 1 then
Delete(ActualLine, 1, 1);
if (Pos(TempVarName_Comp, uppercase(ActualLine))=1) then
begin
ActualLine := TempVarName + VarValue;
// Update list of line numbers holding VarName variables
VarLineNos.Add(IntToStr(LineNo));
VarExists :=true;
end;
end;
end;

// Save first line number in Autoexec.bat which modifies Var environment variable
if VarLineNos.Count > 0 then
FirstVarLineNo := StrToInt(VarLineNos.Strings[0])
else
FirstVarLineNo := 0;

// Only change autoexec if method permit this
if ((Method = vmAddAllways) or ((Method = vmAddOnlyIfVarDoesNotExists) and (VarExists=false))) then
begin
// Write Modified Var back to Autoexec.bat
// First delete all existing Var references from Autoexec.bat
Index := VarLineNos.Count-1;
while (Index>=0) do
begin
AutoexecLines.Delete(StrToInt(VarLineNos.Strings[Index]));
Index := Index-1;
end;
// Then insert new Var variable into Autoexec.bat
if(VarExists=false) then
FirstVarLineNo := AutoexecLines.Count;
AutoexecLines.Insert(FirstVarLineNo, TempVarName+VarValue);
// Delete old Autoexec.bat from disk
if not DeleteFile('c:Autoexec.bat') then
result := mvAutoexecNoWriteAcc;
Sleep(500);
// And finally write Autoexec.bat back to disk
if not (result=mvAutoexecNoWriteAcc) then
SaveStringToFile('c:Autoexec.bat', AutoexecLines.Text, false);
end;

// Free stringlists
VarLineNos.Free;
AutoexecLines.Free;
end; // ModifyVar9x

{ Helper function: Modify Var on Windows NT, 2000 and XP }
function ModifyVarNT(VarName, VarValue: string; Method, Scope: integer): integer;
var
RegRootKey: integer;
RegSubKeyName: string;
RegValueName: string;
ResultVar: string;
OK: boolean;
begin
// Expect everything to be OK
result := mvOK;

// Initialize registry key and value names to reflect if changes should be global or local to current user only
case Scope of
vsCurrentUser:
begin
RegRootKey := HKEY_CURRENT_USER;
RegSubKeyName := 'Environment';
RegValueName := VarName;
end;
vsAllUsers:
begin
RegRootKey := HKEY_LOCAL_MACHINE;
RegSubKeyName := 'SYSTEMCurrentControlSetControlSession ManagerEnvironment';
RegValueName := VarName;
end;
end;

// Read current Var value from registry
OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultVar);

// Write new Var value to registry
if ((Method = vmAddAllways) or ((Method = vmAddOnlyIfVarDoesNotExists) and (OK=false))) then
begin
if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, VarValue) then
begin
result := mvMissingRights;
Exit;
end;
end;
end; // ModifyVarNT

{ Main function: Modify Var }
function ModifyVar(VarName, VarValue: string; Method, Scope: integer): integer;
begin
// Check if both add and remove has been specified (= error!)
if (Method and (vmAddOnlyIfVarDoesNotExists and vmAddAllways)) > 0 then
begin
result := mvBothMethods;
Exit;
end;

// Test if Win9x
if InstallOnThisVersion('4,0','0,0') = irInstall then
ModifyVar9x(VarName,VarValue, Method);

// Test if WinNT, 2000 or XP
if InstallOnThisVersion('0,4','0,0') = irInstall then
ModifyVarNT(VarName, VarValue, Method, Scope);
end; // ModifyVar


96 - HowTo change the mouse cursor of WizardForm

Author: Dicho
Article created: 15/10/05 15:47:47
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=96

The following function allows to change the mouse cursor of an instance of InnoSetup (useful for waiting periods with crHourGlass)

procedure SetControlCursor(control: TWinControl; cursor: TCursor);
var i:Integer;
    wc: TWinControl;
begin
  if (not (control = nil)) then begin
    control.Cursor := cursor;
    try
      for i:=0 to control.ControlCount-1 do begin
        wc := TWinControl(control.Controls[i]);
        if (NOT(wc = nil)) then
          SetControlCursor(wc, cursor)
        else
          control.Controls[i].Cursor := cursor;
      end; {for}
    finally

    end;{try}
  end;{if}
end;{procedure SetControlCursor}

You simply call the procedure by a statement like:

SetControlCursor(WizardForm, crHourGlass);

cheers
Dicho


97 - How to ask a password to uninstall

Article created: 16/10/05 15:22:46
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=97

Some Inno Setup user need to ask for a password to uninstall the software. The code below shows how to create a form, ask for a password, and uninstall software only if the password is correct.

This system is very weak, it's easy to find out the password, so who want to use this way to protect from uninstallation have to code somenthing safer, also if the user want to uninstall and doesn''t know the passwrd, colud anyway delete files of even the folder.
In this sample the uninstall password is removeme.

;
; IS 5.0.8
;

[Setup]
AppName=UninsPassword
AppVerName=UninsPassword
DisableProgramGroupPage=true
DisableStartupPrompt=true
DefaultDirName={pf}\UninsPassword

[Code]
function AskPassword(): Boolean;
var
  Form: TSetupForm;
  OKButton, CancelButton: TButton;
  PwdEdit: TPasswordEdit;
begin

  Result := false;
  Form := CreateCustomForm();
  try
    Form.ClientWidth := ScaleX(256);
    Form.ClientHeight := ScaleY(100);
    Form.Caption := 'Uninstall Password';
    Form.BorderIcons := [biSystemMenu];
    Form.BorderStyle := bsDialog;
    Form.Center;

    OKButton := TButton.Create(Form);
    OKButton.Parent := Form;
    OKButton.Width := ScaleX(75);
    OKButton.Height := ScaleY(23);
    OKButton.Left := Form.ClientWidth - ScaleX(75 + 6 + 75 + 50);
    OKButton.Top := Form.ClientHeight - ScaleY(23 + 10);
    OKButton.Caption := 'OK';
    OKButton.ModalResult := mrOk;
    OKButton.Default := true;

    CancelButton := TButton.Create(Form);
    CancelButton.Parent := Form;
    CancelButton.Width := ScaleX(75);
    CancelButton.Height := ScaleY(23);
    CancelButton.Left := Form.ClientWidth - ScaleX(75 + 50);
    CancelButton.Top := Form.ClientHeight - ScaleY(23 + 10);
    CancelButton.Caption := 'Cancel';
    CancelButton.ModalResult := mrCancel;
    CancelButton.Cancel := True;

    PwdEdit := TPasswordEdit.Create(Form);
    PwdEdit.Parent := Form;
    PwdEdit.Width := ScaleX(210);
    PwdEdit.Height := ScaleY(23);
    PwdEdit.Left := ScaleX(23);
    PwdEdit.Top := ScaleY(23);

    Form.ActiveControl := PwdEdit;

    if Form.ShowModal() = mrOk then
    begin
      Result := PwdEdit.Text = 'removeme';
      if not Result then
            MsgBox('Wrong password it''s not possible uninstall.', mbInformation, MB_OK);
    end;
  finally
    Form.Free();
  end;
end;

function InitializeUninstall(): Boolean;
begin
  Result := AskPassword();
end;


99 - How to test a SQL Server connection using ODBC driver for IS 5

Article created: 17/10/05
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=99

If SQL Server ODBC driver is installed, it is possible ask connection parameters and test if these params are good to connect to Sql Server.

Same code is good to test MSDE connection also, and changing driver string is possible to test any database, it's only necessary the ODBC driver and connection parameters.

To compile this sample you need ODBC function library, the definitio of the additional form is at bottom of the page.

This sample uses also HowTo change the mouse cursor of WizardForm

See also:
How to test a SQL Server connection with ADO

How to test a SQL Server connection using ODBC driver for Inno Setup previous versions.

;
; IS 5.0.8
;

[Setup]
AppName=ODBCtest
AppVerName=ODBCtest
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=false
DisableProgramGroupPage=true
DefaultDirName={pf}ODBCtest
DisableStartupPrompt=true
CreateAppDir=false
OutputBaseFilename=TestODBC
Compression=bzip
AllowUNCPath=false
ShowLanguageDialog=yes

[Code]

// include const and function declarations
#include "odbclib.iss"

var

  // variables

  myserver, myuser, mypwd: String;

//	Parameters for SQL Server
//	DRIVER={SQL Server}
//	SERVER=servername
//	UID=username
//	PWD=password
//	DATABASE=dbname (optional)
//	WSID=workstation id (optional usually computer name)

//	APP=application (optional)
//	NETWORK=Dbmssocn  (optional)
//	ADDRESS=server,port (optional)

{ Protocols
TCP/IP Windows Sockets	Dbmssocn.dll
Named pipes 			Dbnmpntw.dll
Multiprotocol 			Dbmsrpcn.dll
Novell SPX/IPX 			Dbmsspxn.dll
Banyan Vines			Dbmsvinn.dll
DECNet 					Dbmsdecn.dll
AppleTalk 				Dbmsadsn.dll
}

//
// connect to server using usr and pwd
//
function ConnectServer( server, usr, pwd : String ) : Boolean;
var	envH :SQLHANDLE ;	// env handle
    conH :SQLHANDLE;	// connection handle

    sqlret : SQLRETURN;
    hwnd : LongInt;	   // window wizard handle
    constrIN, constrOUT: String;
    sizeout : SQLSMALLINT;
    msg: String;
begin

  hwnd := StrToInt(ExpandConstant('{wizardhwnd}'));
  envH := SQL_NULL_HENV;
  conH := SQL_NULL_HDBC;
  Result := true;

  sqlret := SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, envH );
  if sqlret = SQL_SUCCESS then
  begin

    sqlret := SQLSetEnvAttr( envH, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0 );
      if sqlret = SQL_SUCCESS then
      begin

        sqlret := SQLAllocHandle( SQL_HANDLE_DBC, envH, conH );
        if sqlret = SQL_SUCCESS then
        begin
        constrIN := 'DRIVER={SQL Server};SERVER='+server+';UID='+usr+';PWD='+pwd+';APP=Inno Setup Test;';
        // if it's necessary connect via tcp/ip
        // constrIN := constrIN + 'NETWORK=Dbmssocn;ADDRESS='+server+',1433';
        constrOUT := StringOfChar( ' ', BUFSIZE );

        // connessione
        sqlret := SQLDriverConnect( conH,
                  hwnd,
                  constrIN,
                  Length(constrIN),
                  constrOUT,
                  BUFSIZE - 1,
                  sizeout,
                  SQL_DRIVER_NOPROMPT  );

        constrOUT := CastIntegerToString( CastStringToInteger(constrOUT) );

        msg := #13#10 + #13#10 + GetODBCMsg( SQL_HANDLE_DBC, conH );

        if IsSQLRetOk( sqlret ) then
        begin

          MsgBox( 'Connection string is: ' + #13#10 + #13#10 + constrOUT + msg, mbInformation ,MB_OK );
          sqlret := SQLDisconnect( conH );
        end
        else begin
          MsgBox( 'Connection failed!' + #13#10 + #13#10 + constrIN + msg, mbError , MB_OK );
          sqlret := SQLFreeHandle( SQL_HANDLE_DBC, conH );
          Result := false;
          end;
        end
      else begin
        msg := GetODBCMsg( SQL_HANDLE_DBC, conH );
        MsgBox( 'Error creating CON handle:' + #13#10 + #13#10 + msg, mbError , MB_OK );
        Result := false;
        end;

    end
    else begin
      msg := GetODBCMsg( SQL_HANDLE_ENV, envH );
      MsgBox( 'Error setting ODBC version:' + #13#10 + #13#10 + msg, mbError , MB_OK );
      Result := false;
    end

    // anyway release ENV handle
    sqlret := SQLFreeHandle( SQL_HANDLE_ENV, envH );

  end
  else begin
    msg := GetODBCMsg( SQL_HANDLE_ENV, envH );
    MsgBox( 'Error creating ENV handle:' + #13#10 + #13#10 + msg, mbError , MB_OK );
    Result := false;
  end;

end;

// function by Dicho
// http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=96
//
procedure SetControlCursor(control: TWinControl; cursor: TCursor);
var i:Integer;
    wc: TWinControl;
begin
  if (not (control = nil)) then begin
    control.Cursor := cursor;
    try
      for i:=0 to control.ControlCount-1 do begin
        wc := TWinControl(control.Controls[i]);
        if (NOT(wc = nil)) then
          SetControlCursor(wc, cursor)
        else
          control.Controls[i].Cursor := cursor;
      end; {for}
    finally
    end;{try}
  end;{if}
end;{procedure SetControlCursor}

#include "SrvUsrPwd.isf"

procedure InitializeWizard();
begin
    LoginODBC_CreatePage(wpWelcome);
end;
form SrvUsrPwd.isf

[CustomMessages]
  LoginODBCCaption=Login ODBC Caption
  LoginODBCDescription=Login ODBC description

  [Code]
  var
    lbUsername: TLabel;
    lbServer: TLabel;
    lbPassword: TLabel;
    edUsername: TEdit;
    edServer: TEdit;
    edPassword: TEdit;
    butTest: TButton;

    procedure TestOnClick(Sender: TObject);
    begin
        WizardForm.Enabled := false;

        myserver := edServer.Text;
        myuser := edUsername.Text;
        mypwd := edPassword.Text;

        SetControlCursor(WizardForm, crHourGlass);
        ConnectServer( myserver, myuser , mypwd  );
        SetControlCursor(WizardForm, crDefault);

        WizardForm.Enabled := true;
    end;

  procedure LoginODBC_Activate(Page: TWizardPage);
  begin
  end;

  function LoginODBC_ShouldSkipPage(Page: TWizardPage): Boolean;
  begin
    Result := False;
  end;

  function LoginODBC_BackButtonClick(Page: TWizardPage): Boolean;
  begin
    Result := True;
  end;

  function LoginODBC_NextButtonClick(Page: TWizardPage): Boolean;
  begin
    Result := True;
  end;

  procedure LoginODBC_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: Boolean);
  begin
  end;

  function LoginODBC_CreatePage(PreviousPageId: Integer): Integer;
  var
    Page: TWizardPage;
  begin
    Page := CreateCustomPage(
      PreviousPageId,
      ExpandConstant('{cm:LoginODBCCaption}'),
      ExpandConstant('{cm:LoginODBCDescription}')
    );

    { lbUsername }
    lbUsername := TLabel.Create(Page);
    with lbUsername do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(80);
      Width := ScaleX(48);
      Height := ScaleY(13);
      Caption := 'Username';
      FocusControl := edUsername;
    end;

    { lbServer }
    lbServer := TLabel.Create(Page);
    with lbServer do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(16);
      Width := ScaleX(32);
      Height := ScaleY(13);
      Caption := 'Server';
      FocusControl := edServer;
    end;

    { lbPassword }
    lbPassword := TLabel.Create(Page);
    with lbPassword do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(144);
      Width := ScaleX(46);
      Height := ScaleY(13);
      Caption := 'Password';
      FocusControl := edPassword;
    end;

    { edUsername }
    edUsername := TEdit.Create(Page);
    with edUsername do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(96);
      Width := ScaleX(325);
      Height := ScaleY(21);
      AutoSize := False;
      TabOrder := 1;
      Text := 'sa';
    end;

    { edServer }
    edServer := TEdit.Create(Page);
    with edServer do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(32);
      Width := ScaleX(329);
      Height := ScaleY(21);
      AutoSize := False;
      TabOrder := 0;
      Text := 'localhost';
    end;

    { edPassword }
    edPassword := TEdit.Create(Page);
    with edPassword do
    begin
      Parent := Page.Surface;
      Left := ScaleX(32);
      Top := ScaleY(160);
      Width := ScaleX(325);
      Height := ScaleY(21);
      AutoSize := False;
      PasswordChar := '*';
      TabOrder := 2;
    end;

    // butTest
    butTest := TButton.Create(Page);
    with butTest do
    begin
      Parent := Page.Surface;
      Left := ScaleX(288);
      Top := ScaleY(192);
      Width := ScaleX(73);
      Height := ScaleY(25);
      Caption := 'Test';
      TabOrder := 3;
      OnClick := @TestOnClick;
    end;

    with Page do
    begin
      OnActivate := @LoginODBC_Activate;
      OnShouldSkipPage := @LoginODBC_ShouldSkipPage;
      OnBackButtonClick := @LoginODBC_BackButtonClick;
      OnNextButtonClick := @LoginODBC_NextButtonClick;
      OnCancelButtonClick := @LoginODBC_CancelButtonClick;
    end;

    Result := Page.ID;
  end;


100 - Add a button to License page to print it for IS 5

Article created: 12/11/05 8:35:21
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=100

In the Accept License Page it is possible to add a button, user can click on it and print license text on the default printer.
License is printed using WordPad, so it is possible to print TXT and RTF license text.
See Add a button to License page to print it for previuos versions of Inno Setup.

[Setup]
AppName=PrintLicV5
AppVerName=PrintLicV5
DefaultDirName={pf}\PrintLicV5
DisableStartupPrompt=true
Uninstallable=false
DisableDirPage=true
OutputBaseFilename=PrintLicV5
CreateAppDir=false
DisableProgramGroupPage=true
LicenseFile=MyLicense.rtf

[Files]
Source: MyLicense.rtf; Flags: dontcopy

[Code]

// Print license
procedure PrintButtonClick(Sender: TObject);
var WordpadLoc: String;
    RetCode: Integer;
begin

  if MsgBox( 'Do you want to print License to default printer ?', mbConfirmation, MB_YESNO ) = IDYES then
  begin

    // get WordPad location
    RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WORDPAD.EXE',
      '', WordpadLoc);

    // on NT/2000 it's a REG_EXPAND_SZ, so expand constant ProgramFiles
    StringChange( WordpadLoc, '%ProgramFiles%', ExpandConstant('{pf}') );
    // remove " at begin and end pf string
    StringChange( WordpadLoc, '"', '');

    // extract License file
    try
       ExtractTemporaryFile('MyLicense.rtf');

       if not Exec( WordPadLoc, ' /p ' + ExpandConstant('{tmp}\MyLicense.rtf') ,
         ExpandConstant('{tmp}'), SW_HIDE, ewNoWait , RetCode ) then
       MsgBox('Problems printing License file !' + IntToStr(RetCode), mbError, mb_Ok) ;

  except
    RaiseException('Problem printing license '+#13#13 +
      '(Error ''' + GetExceptionMessage + ''' occurred)');
  end;

  end; // if user want to print the license

end;

procedure InitializeWizard();
var
     PrintButton: TButton;
begin
     PrintButton := TButton.Create(WizardForm.LicensePage);
     PrintButton.Caption := '&Print';
     PrintButton.Left := WizardForm.LicenseMemo.Left +
                         WizardForm.LicenseMemo.Width -
                         PrintButton.Width;
     PrintButton.Top := WizardForm.LicenseMemo.Top +
                        WizardForm.LicenseMemo.Height + 16;
     PrintButton.OnClick := @PrintButtonClick;
     PrintButton.Parent := WizardForm.LicenseAcceptedRadio.Parent;
end;


101 - Create an auto increment build number

Article created: 20/11/05 22:49:54
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=101

It's used ISPP to read and write an INI file that store the build number. The INI file name is BuildInfo.ini and it's stored in the same folder of the iss source file.

; read a previuos Build Number, if not found get 0
#define BuildNum Int(ReadIni(SourcePath	+ "\\BuildInfo.ini","Info","Build","0"))

; add 1 to Build Number
#expr BuildNum = BuildNum + 1

; Store the number in the ini file for the next build
#expr WriteIni( SourcePath	+ "\\BuildInfo.ini","Info","Build", BuildNum )

[Setup]
AppName=AutoIncrBuild
AppVerName=AutoIncrBuild
DisableStartupPrompt=true
Uninstallable=false
CreateAppDir=false
OutputBaseFilename=setup_{#BuildNum}


103 - Add to the setup name the build date and time

Article created: 23/11/05 21:18:40
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=103

The main problem is to fill pre processor variables with information of date and time of build.
To do that it''s possible to create a script that creates an INI file with these information and then in the Inno Setup script read these information and create the build string as you wish.

#define FileIni SourcePath+"\\DateTime2INI.ini"

;exec the script that creates the ini file
#expr Exec("cscript.exe", SourcePath+"\\DateTime2INI.vbs", SourcePath )

; read infos in variables
#define BuildYear   ReadIni(FileIni , "DateTime" , "Year" )
#define BuildMonth  ReadIni(FileIni , "DateTime" , "Month" )
#define BuildDay    ReadIni(FileIni , "DateTime" , "Day" )
#define BuildHour   ReadIni(FileIni , "DateTime" , "Hour" )
#define BuildMinute ReadIni(FileIni , "DateTime" , "Minute" )
#define BuildSecond ReadIni(FileIni , "DateTime" , "Second" )

;make the Build String
#define BuildStr BuildYear+"_"+BuildMonth+"_"+BuildDay+"_"+BuildHour+"_"+BuildMinute+"_"+BuildSecond

[Setup]
AppName=OutputTimeBuild
AppVerName=OutputTimeBuild {#BuildStr}
DisableStartupPrompt=true
Uninstallable=false
CreateAppDir=false
OutputBaseFilename=setup_{#BuildStr}

vbscript DateTime2INI.vbs

'create an INI file with information about date and time
'for easy access from Inno Setup PreProcessor

Dim fso, ts
Const ForWriting = 2
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile("DateTime2INI.ini", ForWriting, True)

ts.WriteLine("")
ts.WriteLine("[DateTime]")
ts.WriteLine("Year=" & Year(Now()) )
ts.WriteLine("Month=" & Month(Now()) )
ts.WriteLine("Day=" & Day(Now()) )
ts.WriteLine("Hour=" & Hour(Now()) )
ts.WriteLine("Minute=" & Minute(Now()) )
ts.WriteLine("Second=" & Second(Now()) )

ts.close
set fso = nothing

sample of DateTime2INI.ini

[DateTime]
Year=2005
Month=11
Day=23
Hour=21
Minute=8
Second=35


104 - How to get Date and Time of a file for IS 5

Article created: 07/01/06 23:06:42
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=104

It is possible to get Date and Time of file, script read Creation Time, Access Time and Modification Time.

To compile this sample you need LangLib.iss

Portion of code Renato Cazzaro

Code for Inno Setup version 4 is How to get Date and Time of a file

;
; Inno Setup 5.1.5
;
;
; Portion of code Renato Cazzaro
; http://news.jrsoftware.org/news/innosetup.code/msg03380.html
;

[Setup]
AppName=FileTimeV5
AppVerName=FileTimeV5
Uninstallable=false
UpdateUninstallLogAppName=false
DisableDirPage=true
DisableProgramGroupPage=true
DefaultDirName={pf}\FileTimeV5
DisableStartupPrompt=true

#include "LangLib.iss"

[Code]

type

TFileTime = Record
    lowdatatime: longint;
    highdatatime: longint;
End;

TSystemTime = record
    wYear : Word;
    wMonth : Word;
    wDayOfWeek : Word;
    wDay : Word;
    wHour : Word;
    wMinute : Word;
    wSecond : Word;
    wMilliseconds : Word;
end;

function GetFileTime(hfile: longint; var lpcreation,lpaccess,lpwrite:TFileTime): boolean;
external 'GetFileTime@kernel32.dll stdcall';

function FileTimeToSystemTime(var tftm: TFileTime; var systm: TSystemTime): boolean;
external 'FileTimeToSystemTime@kernel32.dll stdcall';

function FileTimeToLocalFileTime( utct: TFileTime; var loct: TFileTime): boolean;
external 'FileTimeToLocalFileTime@kernel32.dll stdcall';

function GetDateFormat(Locale: Integer;dwFlags: LongInt;
   var lpDate: TSystemTime; lpFormat: PChar; lpDateStr: String;
   cchDate: Integer): Integer; external
   'GetDateFormatA@kernel32.dll';

function GetTimeFormat(Locale: Integer;dwFlags: LongInt;
   var lpTime: TSystemTime;lpFormat: PChar;lpTimeStr: String;
   cchDate: Integer): Integer; external
   'GetTimeFormatA@kernel32.dll';

function FormattedTime( t: TSystemTime ) : String;
var
    s, fmt: String;
begin

        fmt := StringOfChar(' ',64);
        GetDateFormat( GetUserDefaultLCID , DATE_LONGDATE, t, '' , fmt ,63);
        fmt := CastIntegerToString( CastStringToInteger(fmt) );

        s := fmt;

        fmt := StringOfChar(' ',64);
        GetTimeFormat( GetUserDefaultLCID , 0, t , '' , fmt ,63);
        fmt := CastIntegerToString( CastStringToInteger(fmt) );

        s := s + ' ' + fmt;

    Result := s;
end;

Function GetFileSysTime( nomef: String ; var stime: String; idx : Integer  ): TSystemTime;
Var
    fs: TFileStream;
    risul: TSystemTime;
    creat, access, write, local: TFileTime;
Begin
    fs := TFileStream.Create(nomef,fmOpenRead);
    GetFileTime(fs.handle,creat,access,write);
    fs.Free;
    if idx = 1 then FileTimeToLocalFileTime(creat,local);
    if idx = 2 then FileTimeToLocalFileTime(access,local);
    if idx = 3 then FileTimeToLocalFileTime(write,local);
    FileTimeToSystemTime(local,risul);
    stime := FormattedTime( risul );
    Result := risul;
End;

var
  lbFile: TLabel;
  lbDateCreated: TLabel;
  lbDateModified: TLabel;
  Label2: TLabel;
  butChoose: TButton;
  edFile: TEdit;
  edCreTime: TEdit;
  edModTime: TEdit;
  edAccessTime: TEdit;

procedure ChooseFile(Sender: TObject);
var
    filename, s : String;
    t  : TSystemTime;
begin

    if GetOpenFileName( 'Choose a file', filename,
        GetShellFolder( false, sfDocs ) , 'Text files (*.txt)|*.txt|All files (*.*)|*.*' , '*.txt') then
    begin

        edFile.text := filename;
        t := GetFileSysTime(filename, s, 1 );
        edCreTime.text := s;
        t := GetFileSysTime(filename, s, 2 );
    edAccessTime.text := s;
    t := GetFileSysTime(filename, s, 3 );
    edModTime.text := s;

    end;

end;

function FileTime_CreatePage(PreviousPageId: Integer): Integer;
var
  Page: TWizardPage;
begin
  Page := CreateCustomPage(
    PreviousPageId,
    ExpandConstant('Time of a file'),
    ExpandConstant('Date and time of a file')
  );

  { lbFile }
  lbFile := TLabel.Create(Page);
  with lbFile do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 56;
    Width := 415; Height := 13;
    AutoSize := False;
    Caption := 'File selected:';
  end;

  { lbDateCreated }
  lbDateCreated := TLabel.Create(Page);
  with lbDateCreated do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 104;
    Width := 199;  Height := 13;
    AutoSize := False;
    Caption := 'Creation Time';
  end;

  { lbDateModified }
  lbDateModified := TLabel.Create(Page);
  with lbDateModified do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 152;
    Width := 199;  Height := 13;
    AutoSize := False;
    Caption := 'Modification Time';
  end;

  { Label2 }
  Label2 := TLabel.Create(Page);
  with Label2 do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 192;
    Width := 199;  Height := 13;
    AutoSize := False;
    Caption := 'Access Time';
  end;

  { butChoose }
  butChoose := TButton.Create(Page);
  with butChoose do
  begin
    Parent := Page.Surface;
    Left := 88;  Top := 16;
    Width := 233;  Height := 25;
    Caption := 'Choose a file...';
    TabOrder := 0;
    OnClick := @ChooseFile;
  end;

  { edFile }
  edFile := TEdit.Create(Page);
  with edFile do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 72;
    Width := 409;  Height := 21;
    TabStop := False;
    ReadOnly := True;
    TabOrder := 1;
  end;

  { edCreTime }
  edCreTime := TEdit.Create(Page);
  with edCreTime do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 120;
    Width := 201;  Height := 21;
    TabStop := False;
    ReadOnly := True;
    TabOrder := 2;
  end;

  { edModTime }
  edModTime := TEdit.Create(Page);
  with edModTime do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 168;
    Width := 201;  Height := 21;
    TabStop := False;
    ReadOnly := True;
    TabOrder := 3;
  end;

  { edAccessTime }
  edAccessTime := TEdit.Create(Page);
  with edAccessTime do
  begin
    Parent := Page.Surface;
    Left := 0;  Top := 208;
    Width := 201;  Height := 21;
    TabStop := False;
    ReadOnly := True;
    TabOrder := 4;
  end;

  Result := Page.ID;
end;

procedure InitializeWizard();
begin
    FileTime_CreatePage(wpWelcome);
end;


105 - How do I get the list of files from the [Files] section into the [Code] section

Article created: 29/01/06 17:29:32
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=105

Michael Winestock wrote:

I have seen the question "How do I get the list of files from the [Files] section into the [Code] section" come up several times recently.
Actually, I had to answer this question as I needed to use this for an installer I am creating at work. I came up with a pretty clever method that will automatically read the script file and return back a list of files that can be used in the [Code] section. Best part you don''t need to hard code anything to get the list of files or specifically know the
name of the script file being used. The ISPP is used.

I am including three files for you to look at including a test script file (test.iss) that shows how to use it.
test.iss

[Setup]
AppName=Test
AppId=Test
AppVerName=Test 1.0
DefaultDirName="C:\Test"
DefaultGroupName=Test
OutputBaseFilename=Test Install
OutputDir = "."

[Files]
Source: test.iss; DestDir: {app}

[_EndOfScript]

[Code]
// Always include autoProcessScript.iss after the [Files] section.
#include "autoProcessScript.iss"
#include "setupFileList.iss"

function InitializeSetup() : Boolean;
var
    listLen  : Integer;
    fileList : TSetupFileList;

begin
    Result   := True;
    fileList := GetSetupFileList();
    listLen  := GetArrayLength(fileList);
end;
[/Code]
setupFileList.iss

//*****************************************************************************
//
//  File: setupFileList.iss
//
//  Description: This file contains support code to return a list of filenames
//               specified in the [Files] and [Versions] sections of the
//               script file.
//
//  Global Routines:
//      GetSetupFileList
//
//  Internal Only Routines:
//      CreateSetupFileList
//
//  Notes:
//
//  History:
//
//*****************************************************************************

type
    // Define a record to hold the informatiom about the files listed in the
    // [Files] section of the script file.
    TSetupFileInfo = record
        Name    : String;
        DestDir : String;
        Version : String;
    end;

    TSetupFileList = Array of TSetupFileInfo;

var
    // Define static variables.
    s_setupFileList : TSetupFileList;

// Function prototypes.
procedure CreateSetupFileList(); forward;
function  GetSetupFileList() : TSetupFileList; forward;

//*****************************************************************************
//
//  Routine: CreateSetupFileList
//
//  Description: Creates the list of setup files, that have versions.
//
//  Calling Sequence:
//      CreateSetupFileList();
//
//  Parameters:
//
//  Routines Called:
//      GetSetupFileList
//      GetSetupFileListLen
//
//  Global Variables Referenced:
//      s_setupFileList
//
//  Notes:
//
//  Return Values:
//
//*****************************************************************************

procedure CreateSetupFileList();
var
    entry         : Integer;
    copyLen       : Integer;
    destLen       : Integer;
    fileLen       : Integer;
    listLen       : Integer;
    versionLen    : Integer;
    dest          : String;
    version       : String;
    filename      : String;
    fileList      : String;

begin
    // Get the list of files that setup will be installing.
    fileList := '{# GetSetupFileList()}';
    listLen  := {# GetSetupFileListLen()};

    // Initialize the module list array.
    SetArrayLength(s_setupFileList, listLen);

    // Process the entries in the file list.
    for entry := 0 to listLen - 1 do
    begin
        // Pull the next filename from the file list.
        fileLen  := Pos(',', fileList);
        filename := Copy(fileList, 1, fileLen - 1);
        copyLen  := Length(fileList) - fileLen;
        fileList := Copy(fileList, fileLen + 1, copyLen);    // Strip filename from file list.

        // Pull the next filename destination from the file list.
        destLen  := Pos(',', fileList);
        dest     := Copy(fileList, 1, destLen - 1);
        copyLen  := Length(fileList) - destLen;
        fileList := Copy(fileList, destLen + 1, copyLen);    // Strip destination from file list.

        // Pull the next filename version from the file list.
        versionLen := Pos('|', fileList);

        if (versionLen = 0) then
            // The length of the version is the remaining length of the file
            // list.
            versionLen := Length(fileList) + 1;

        version  := Copy(fileList, 1, versionLen - 1);
        copyLen  := Length(fileList) - versionLen;
        fileList := Copy(fileList, versionLen + 1, copyLen); // Strip version from the file list.

        // Create the file list entry.
        s_setupFileList[entry].Name    := filename;
        s_setupFileList[entry].DestDir := dest;
        s_setupFileList[entry].Version := version;
    end;
end;

//*****************************************************************************
//
//  Routine: GetSetupFileList
//
//  Description: Get the list of setup files, that have versions.
//
//  Calling Sequence:
//      list := GetSetupFileList();
//
//  Parameters:
//
//  Routines Called:
//      CreateSetupFileList
//
//  Global Variables Referenced:
//      s_setupFileList
//
//  Notes:
//
//  Return Values:
//      TSetupFileList - The list of versioned setup files.
//
//*****************************************************************************

function GetSetupFileList() : TSetupFileList;
begin
    // Check to see if the setup file list has been created.
    if (GetArrayLength(s_setupFileList) = 0) then
        // Setup file list has not been created yet so go and create it first.
        CreateSetupFileList();

    Result := s_setupFileList;
end;
autoProcessScript.iss

[/Code]
;*****************************************************************************
;
;  File: autoProcessScript.iss
;
;  Description: This file contains the Inno Setup preprocessor code to read out
;               of a Inno Setup script the list of files listed in the [Files]
;               section of a script file.
;
;  Global Routines:
;      GetSetupFileList
;      GetSetupFileListLen
;
;  Internal Only Routines:
;      DirExist
;      GetTempDir
;      OutputMessage
;      GetFilenameIndex
;      IsEmpty
;      Trim
;      TrimLeft
;      TrimRight
;      GetTagValue
;      ProcessFilesSection
;      AutoProcessScriptMain
;
;  Notes:
;      This script is processed by the Inno Setup Preprocessor.  It is intended
;      to be included into a Inno Setup script.
;
;  History:
;
;*****************************************************************************

; Define the maximum number of script files that may be listed in the [Files]
; section of the script.
#define protected MAX_SCRIPT_FILES  50

; Define global macro variables.
#define public gm_fileList       ""
#define public gm_fileListCount  0

; Temporary storage used to hold info about the install files.
#dim protected gm_fileListName[MAX_SCRIPT_FILES]
#dim protected gm_fileListDest[MAX_SCRIPT_FILES]
#dim protected gm_fileListVersion[MAX_SCRIPT_FILES]

;*****************************************************************************
;
;  Routine: GetSetupFileList
;
;  Description: Returns the list of file name, dest path, and version of the
;               entries listed in the [Files] and [Versions] sections of a
;               script file.
;
;  Calling Sequence:
;      list := '{# GetSetupFileList()}';
;
;  Parameters:
;
;  Routines Called:
;
;  Global Variables Referenced:
;      gm_fileList
;
;  Notes:
;
;  Return Values:
;      Returns a string list of files in the format:
;          filename,dest,version[|filename,dest,version[|...]]
;
;*****************************************************************************

#define public GetSetupFileList() \
    gm_fileList

;*****************************************************************************
;
;  Routine: GetSetupFileListLen
;
;  Description: Returns the number of entries contained in the module list
;               returned by the GetSetupFileList macro.
;
;  Calling Sequence:
;      listLen := '{# GetSetupFileListLen()}';
;
;  Parameters:
;
;  Routines Called:
;
;  Global Variables Referenced:
;      gm_fileListCount
;
;  Notes:
;
;  Return Values:
;      Integer - Number of entries contained in the list returned by
;                GetSetupFileList.
;
;*****************************************************************************

#define public GetSetupFileListLen() \
    gm_fileListCount

;*****************************************************************************
;
;                     I N T E R N A L   R O U T I N E S
;
;*****************************************************************************

;*****************************************************************************
;
;  Routine: DirExist
;
;  Description: Determine if a directory exists.
;
;  Calling Sequence:
;      #expr exist = DirExist(dirname)
;
;  Parameters:
;      dirname - Parameter variable containing the name of the directory.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;      True if the specified directory name exists.
;
;*****************************************************************************

#define protected DirExist(str dirname) \
    ((dirname != "") && (FindFirst(dirname, faDirectory) != 0))

;*****************************************************************************
;
;  Routine: GetTempDir
;
;  Description: Returns the temporary directory name.
;
;  Calling Sequence:
;      #expr tmpDir = GetTempDir()
;
;  Parameters:
;
;  Routines Called:
;      DirExist
;
;  Global Variables Referenced:
;
;  Notes:
;      Gets the temporary file path as follows:
;          1. The path specified by the TMP environment variable.
;          2. The path specified by the TEMP environment variable, if TMP is
;             not defined or if TMP specifies a directory that does not exist.
;          3. The current directory, if both TMP and TEMP are not defined or
;             specify nonexistent directories.
;
;  Return Values:
;      String - The fully qualified path of the temporary directory including
;               a trailing backslash.
;
;*****************************************************************************

#define protected GetTempDir() \
    _GetTempDir_,              \
    r_GetTempDir

; Define the return variable.
#define protected r_GetTempDir

#sub _GetTempDir_
    ; Define local variables.
    #define private entry
    #define private tmpDirFound
    #dim    private envPaths[3]

    ; Build the list of environment point to the temp directory.
    #expr envPaths[0] = "TMP"
    #expr envPaths[1] = "TEMP"
    #expr tmpDirFound = False

    ; Attempt to get the temp directory from the user environment.
    #for {entry = 0; (! tmpDirFound) && (envPaths[entry] != ""); entry++} \
        r_GetTempDir = GetEnv(envPaths[entry]),                           \
        tmpDirFound  = DirExist(r_GetTempDir)

    #if (! tmpDirFound)
        ; The temp directory is not defined in the user environment so use the
        ; current directory instead.
        #expr r_GetTempDir = "."
    #endif

    ; Make sure the temp directory has a trailing backslash.
    #expr r_GetTempDir = AddBackSlash(r_GetTempDir)
#endsub

;*****************************************************************************
;
;  Routine: OutputMessage
;
;  Description: Outputs a message to the screen.
;
;  Calling Sequence:
;      #expr OutputMessage(message)
;
;  Parameters:
;      message - Parameter variable containing the message to output to the
;                screen.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected OutputMessage(str message) \
    pom_message = message,                   \
    _OutputMessage_

; Define the parameter variables.
#define protected pom_message

#sub _OutputMessage_
    #pragma message pom_message
#endsub

;*****************************************************************************
;
;  Routine: GetFilenameIndex
;
;  Description: Returns the index location in the file list based on the
;               filename.
;
;  Calling Sequence:
;      #expr index = GetFilenameIndex(filename)
;
;  Parameters:
;      filename - Parameter variable containing the filename to find.
;
;  Routines Called:
;
;  Global Variables Referenced:
;      gm_fileListCount
;      gm_fileListName
;
;  Notes:
;
;  Return Values:
;      Integer - The index in the file list.
;
;*****************************************************************************

#define protected GetFilenameIndex(str filename) \
    pgfi_filename = filename,                    \
    _GetFilenameIndex_,                          \
    r_GetFilenameIndex

; Define the return variable.
#define protected r_GetFilenameIndex

; Define the parameter variables.
#define protected pgfi_filename

#sub _GetFilenameIndex_
    ; Define the local variables
    #define private entry
    #define private found

    ; Search the file list for the passed in filename.
    #expr found = False

    #for {entry = 0; (! found) && (entry < gm_fileListCount); entry++} \
        found = (LowerCase(pgfi_filename) == LowerCase(gm_fileListName[entry]))

    ; If the filename was not found in the file list, store filename in the file list.
    #if (! found)
        ; Keep track of how many entries are stored in the file list.
        #expr gm_fileListCount++

        ; Point to where to store the filename.  Store the filename in the file list.
        #expr entry                      = gm_fileListCount
        #expr gm_fileListName[entry - 1] = pgfi_filename
    #endif

    ; Return the index of where the filename resides in the file list.
    #expr r_GetFilenameIndex = entry - 1
#endsub

;*****************************************************************************
;
;  Routine: IsEmpty
;
;  Description: Determines if a line of text is empty.
;
;  Calling Sequence:
;      #expr value = IsEmpty(line)
;
;  Parameters:
;      line - The line of text to check.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     Boolean - True if the line starts with a comment (;) or is blank, False
;               otherwise.
;
;*****************************************************************************

#define protected IsEmpty(str line) \
    Local[0] = Copy(line, 1, 1),    \
    ((Local[0] == '') || (Local[0] == ';'))

;*****************************************************************************
;
;  Routine: Trim
;
;  Description: Strips off all spaces from the left and right side of a string.
;
;  Calling Sequence:
;      #expr trimResult = Trim(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;      trimLeft
;      trimRight
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected Trim(str value) \
    Local[0] = TrimLeft(value),   \
    TrimRight(Local[0])

;*****************************************************************************
;
;  Routine: TrimLeft
;
;  Description: Strips off all spaces from the left side a string.
;
;  Calling Sequence:
;      #expr trimResult = TrimLeft(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected TrimLeft(str value) \
    ptl_trimStr = value,              \
    _TrimLeft_,                       \
    r_TrimLeft

; Define the return variable.
#define protected r_TrimLeft

; Define the parameter variables.
#define protected ptl_trimStr

#sub _TrimLeft_
    ; Define the local variables.
    #define private startAt
    #define private copyLen

    ; Get the length of the string to trim.
    #expr copyLen = Len(ptl_trimStr)

    ; Skip all spaces off the left side of the string.
    #for {startAt = 1; \
          (startAt <= copyLen) && (Copy(ptl_trimStr, startAt, 1) == " "); \
          1} \
        startAt++, \
        copyLen--

    ; Actually get rid of any spaces at left side of the string.
    #expr r_TrimLeft = Copy(ptl_trimStr, startAt, copyLen)
#endsub

;*****************************************************************************
;
;  Routine: TrimRight
;
;  Description: Strips off all spaces off the right side of a string.
;
;  Calling Sequence:
;      #expr trimResult = TrimRight(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected TrimRight(str value) \
    ptr_trimStr = value,               \
    _TrimRight_,                       \
    r_TrimRight

; Define the return variable.
#define protected r_TrimRight

; Define the parameter variables.
#define protected ptr_trimStr

#sub _TrimRight_
    ; Define the local variables.
    #define private copyLen

    ; Get the length of the string to trim.
    #expr copyLen = Len(ptr_trimStr)

    ; Skip all spaces at the right side of the string.
    #for {1; (copyLen >= 1) && (Copy(ptr_trimStr, copyLen, 1) == " "); 1} \
        copyLen--

    ; Actually get rid of any spaces on the right side of the string.
    #expr r_TrimRight = Copy(ptr_trimStr, 1, copyLen)
#endsub

;*****************************************************************************
;
;  Routine: GetTagValue
;
;  Description: Returns the value associated with a tag string from a line of
;               text.
;
;  Calling Sequence:
;      #expr value = GetTagValue(tagStr, line)
;
;  Parameters:
;      tagStr - Parameter variable containing the tag value to find.
;      line   - Parameter variable containing the line of text.
;
;  Routines Called:
;      Trim
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;      String - The value associated with the found tag string.  An empty ("")
;               is returned if the tag string could not be found.
;
;*****************************************************************************

#define protected GetTagValue(str tag, str line) \
    pgtv_tagStr  = tag,                          \
    pgtv_lineStr = line,                         \
    _GetTagValue_,                               \
    r_GetTagValue

; Define the return variable.
#define protected r_GetTagValue

; Define the parameter variables.
#define protected pgtv_tagStr
#define protected pgtv_lineStr

#sub _GetTagValue_
    ; Define the local variables.
    #define private tagEnd
    #define private copyLen
    #define private lineStr
    #define private tagStart

    ; Just in case the tag could not be found.
    #expr r_GetTagValue = ""

    ; Find where the next tag value is specified.
    #expr tagStart = Pos(LowerCase(pgtv_tagStr), LowerCase(pgtv_lineStr))

    ; Only care about non-blank lines.
    #if (tagStart > 0)
        ; Shift the line so that the tag value can be processed.
        #expr copyLen = Len(pgtv_lineStr) - tagStart + 1
        #expr lineStr = Copy(pgtv_lineStr, tagStart, copyLen)

        ; Figure out where the tag value starts and ends.
        #expr tagStart = Len(pgtv_tagStr) + 1
        #expr tagEnd   = Pos(";", lineStr)

        #if (tagEnd == 0)
            ; The end of the tag value is not marked so the length is from where
            ; the tag value actually starts until the end of the line.
            #expr tagEnd = Len(lineStr) + 1
        #endif

        ; Copy off the the tag value.
        #expr copyLen       = tagEnd - tagStart
        #expr r_GetTagValue = Copy(lineStr, tagStart, copyLen)

        ; Trim spaces off of the tag value.
        #expr r_GetTagValue = Trim(r_GetTagValue)
    #endif
#endsub

;*****************************************************************************
;
;  Routine: ProcessFilesSection
;
;  Description: Retrieves the filename, destination and version of the entries
;               in the [Files] section of a script file.
;
;  Calling Sequence:
;      #expr ProcessFilesSection(line)
;
;  Parameters:
;      line - Parameter variable containing the line from the script file.
;
;  Routines Called:
;      GetTagValue
;      GetFilenameIndex
;
;  Global Variables Referenced:
;      gm_fileListDest
;      gm_fileListVersion
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected ProcessFilesSection(str line) \
    ppfs_lineStr = line,                        \
    _ProcessFilesSection_

; Define the parameter variables.
#define protected ppfs_lineStr

; Define the static variables.
#define protected spfs_inFilesSection False

#sub _ProcessFilesSection_
    ; Define the local variables.
    #define private entry
    #define private sourceDest
    #define private sourceFile
    #define private sourceVersion

    ; Found a new section?
    #if ((Pos("[", ppfs_lineStr) > 0) && (Pos("]", ppfs_lineStr) > 0))
        ; Found the [Files] section?
        #expr spfs_inFilesSection = (Pos("files", LowerCase(ppfs_lineStr)) > 0)

    #elif (spfs_inFilesSection)
        ; Get the next source file name.
        #expr sourceFile = GetTagValue("Source:", ppfs_lineStr)

        ; Get the destination for the source file.
        #expr sourceDest = GetTagValue("DestDir:", ppfs_lineStr)

        ; Skip the entries which don't have a specified destination.
        #if (sourceDest != "")
            ; Retrieve from the install file its encoded version if available.
            ; Some files don't have encoded versions.
            #expr sourceVersion = GetFileVersion(sourceFile)

            ; Only want the basename portion of the filename.
            #expr sourceFile = ExtractFileName(sourceFile)

            ; Get the index for the source file entry in he file list.
            #expr entry = GetFilenameIndex(sourceFile)

            ; Store off the source file destinaton in the file list.
            #expr gm_fileListDest[entry] = sourceDest

            ; Store off the source file encoded version in the file list.
            #expr gm_fileListVersion[entry] = sourceVersion
        #endif
    #endif
#endsub

;*****************************************************************************
;
;  Routine: AutoProcessScriptMain
;
;  Description: Reads and processes lines from a script file.
;
;  Calling Sequence:
;      #expr AutoProcessScriptMain()
;
;  Parameters:
;
;  Routines Called:
;      GetTempDir
;      TrimLeft
;      ProcessFilesSection
;      OutputMessage
;
;  Global Variables Referenced:
;      gm_fileList
;      gm_fileListName
;      gm_fileListDest
;      gm_fileListVersion
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected AutoProcessScriptMain() \
    _AutoProcessScriptMain_

#sub _AutoProcessScriptMain_
    ; Define the local variables.
    #define private entry
    #define private tmpDir
    #define private nextLine
    #define private separator
    #define private scriptFile
    #define private scriptFilename

    ; Get the temp directory path.
    #expr tmpDir = GetTempDir()

    ; The script file will be stored to and read from the tmp directory.
    #expr scriptFilename = tmpDir + "_script_.iss"

    ; Save off a copy of the script to the temporary file.  This is done
    ; so that macros can be replaced by the preprocessor before the script is
    ; processed.
    #expr SaveToFile(scriptFilename)

    ; Open the script file to be processed.
    #expr scriptFile = FileOpen(scriptFilename)

    #if (! scriptFile)
        ; Should never happen but just in case...
        #expr ErrorMessage("Unable to open " + scriptFilename)

    #else
        ; Process all the lines in the [Files] section of the script file.  Only
        ; want to process those lines that are not commented out or blank.
        #for {1; (! FileEof(scriptFile)); 1}    \
            nextLine = FileRead(scriptFile),    \
            nextLine = TrimLeft(nextLine),      \
            (! IsEmpty(nextLine))               \
                ? ProcessFilesSection(nextLine) \
                : True

        ; Done with the file, close and delete it.
        #expr FileClose(scriptFile)
        #expr DeleteFile(scriptFilename)
    #endif

    ; Build the returnable list of file entries as the following format:
    ;     <filename>,<dest>,<version>[|<filename>,<dest>,<version>[|...]]
    #expr separator   = ""
    #expr gm_fileList = ""

    #for {entry = 0; entry < gm_fileListCount; entry++}           \
        gm_fileList = gm_fileList + separator,                    \
        gm_fileList = gm_fileList + gm_fileListName[entry] + ",", \
        gm_fileList = gm_fileList + gm_fileListDest[entry] + ",", \
        gm_fileList = gm_fileList + gm_fileListVersion[entry],    \
        separator   = "|",                                        \
                                                                  \
        OutputMessage("Found: " + gm_fileListName[entry] + " (" + gm_fileListVersion[entry] + ")")
#endsub

;*****************************************************************************
;
;                      M A I N   C A L L I N G   P O I N T
;
;*****************************************************************************

#expr OutputMessage("######## Begin Reading List of Setup Files ########")
#expr OutputMessage("")

; Auto call the main entry point.
#expr AutoProcessScriptMain()

#expr OutputMessage("")
#expr OutputMessage("######## End Reading List of Setup Files ########")
[Code]


107 - How to detect if an application is running with code

Article created: 02/02/06 23:15:19
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=107

There are many ways to How to detect if an application is running however if you need to detect if an exe is running both in Win9X systems, and Win2K/WinXP you need psvince because depending on OS type it''s necessary to link some function on kernel32 for Win9X systems, or psapi.dll on Win2K or later operating systems.
If don''t need to install on Win9X or WinNT it is possible to use just code to detect if an exe is running.

Michael Winestock published this code on the Inno Setup newsgroup http://news.jrsoftware.org/news/innosetup/msg53256.html I changed small part of the code so there is no need to install the psapi.dll and it is possible to use same function at install and uninstall time.

The sample check if notepad.exe is running so to install or uninstall it is necessary that notepad is not running, look at similar sample using psvince.dll How to call psvince.dll on install and uninstall

;
; IS 5.1,5
;
; portion of code Michael Winestock
; http://news.jrsoftware.org/news/innosetup/msg53256.html

[Setup]
AppName=Psapi
AppVerName=Psapi
DisableProgramGroupPage=true
DisableStartupPrompt=true
DefaultDirName={pf}\Psapi

[Code]
type
   HANDLE   = LongInt;
   HMODULE  = HANDLE;
   pHMODULE = Array[0..255] of HMODULE;
   LPDWORD  = DWORD;
   PDWORD   = Array[0..255] of DWORD;
   LPTSTR   = String;

const

   PROCESS_QUERY_INFORMATION = $400;
   PROCESS_VM_READ           = $10;
   MAX_LENGTH                = 255;

function OpenProcess(dwDesiredAccess : DWORD; bInheritHandle : Boolean; dwProcessId : DWORD) : HANDLE;
external 'OpenProcess@kernel32.dll stdcall';

function CloseHandle(hObject : HANDLE) : BOOL;
external 'CloseHandle@kernel32.dll stdcall';

function EnumProcesses(var pProcessIds : PDWORD; cb : DWORD; var pBytesReturned : DWORD) : BOOL;
external 'EnumProcesses@psapi.dll stdcall';

function EnumProcessModules(hProcess : HANDLE; var lphModule : pHMODULE; cb : DWORD; var lpcbNeeded : LPDWORD) : BOOL;
external 'EnumProcessModules@psapi.dll stdcall';

function GetModuleFileNameExA(hProcess : HANDLE; hModule : HMODULE; lpFilename : LPTSTR; nSize : DWORD) : DWORD;
external 'GetModuleFileNameExA@psapi.dll stdcall';

function IsRunning(procName : String; fullPath : Boolean) : Boolean;
var
   found       : Boolean;
   filenameEnd : Integer;
   filename    : String;
   NA          : DWORD;
   entry       : DWORD;
   maxEntries  : DWORD;
   processes   : PDWORD;
   lpFilename  : LPTSTR;
   hProcess    : HANDLE;
   hMod        : pHMODULE;

begin

  SetLength(lpFilename, MAX_LENGTH);
  found := False;

  if (EnumProcesses(processes, SizeOf(processes), maxEntries)) then
  begin
    entry      := 0;
    maxEntries := maxEntries / SizeOf(maxEntries);

    while ((not found) and (entry < maxEntries)) do
    begin

      hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, processes[entry]);
      if (hProcess <> 0) then
      begin

      if (EnumProcessModules(hProcess, hMod, SizeOf(hMod), NA)) then
        if (GetModuleFileNameExA(hProcess, hMod[0], lpFilename, Length(lpFilename)) > 0) then
          begin
            filenameEnd := Pos(#0, lpFilename);
            filename := Copy(lpFilename, 1, filenameEnd - 1);

            if (not fullpath) then
              filename := ExtractFileName(filename);

            if (LowerCase(filename) = LowerCase(procName)) then
              found := True;
          end;

      CloseHandle(hProcess);
      end;

      entry := entry + 1;

    end;
  end;

  Result := found;
end;

function CheckForRunningModules(const messageId : TSetupMessageID) : Boolean;
var
   moduleRunning : Boolean;
   messageStr    : String;
   moduleName    : String;

begin
  Result     := True;
  moduleName := 'notepad.exe';
  messageStr := FmtMessage(SetupMessage(messageId), [moduleName]);

  Repeat
    moduleRunning := IsRunning(moduleName, False);

    if (moduleRunning) then
      if (MsgBox(messageStr, mbError, MB_OKCANCEL) = IDCANCEL) then
        Result := False;
  Until ((not moduleRunning) or (not Result));
end;

function InitializeSetup() : Boolean;
begin
   Result := CheckForRunningModules(msgSetupAppRunningError);
end;

function InitializeUninstall() : Boolean;
begin
   Result := CheckForRunningModules(msgUninstallAppRunningError);
end;


108 - PostgreSQL setup built with Inno Setup

Article created: 03/02/06 23:28:03
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=108

Tony Caduto made a script to install PostgreSQL DBMS with Inno Setup.

Original post PostgreSQL setup built with Inno finished.

This is the main script, it is possible to download the complete package 192 KB.

;This setup is released under the BSD license and may be used for embedding a PostgreSQL install within your application.
;A copy of the license is in the source distribution

[Setup]
;Only allow running on windows 2000 and up
MinVersion=0.0,5.0
;Only allow setup to be run by a user with admin rights
PrivilegesRequired=admin
AppName=PostgreSQL Win32 8.1 Server
AppVerName=PostgreSQL Win32 8.1 Server
AppPublisher='AM Software Design';
AppPublisherURL=http://www.amsoftwaredesign.com
AppSupportURL=http://www.amsoftwaredesign.com
AppUpdatesURL=http://www.amsoftwaredesign.com
DefaultDirName={pf}\Postgresql\8.1
DefaultGroupName=Postgresql
OutputBaseFilename=postgresql-8.1
Compression=lzma
SolidCompression=yes
WizardImageFile=C:\Documents and Settings\tony\My Documents\Inno Setup Projects\pg_installer_setup\large_image.bmp
LicenseFile=C:\Documents and Settings\tony\My Documents\Inno Setup Projects\pg_installer_setup\license.rtf
SourceDir=C:\Program Files\pgsql_install
OutputDir=C:\Documents and Settings\tony\My Documents\Inno Setup Projects\output
SetupIconFile=C:\Documents and Settings\tony\My Documents\Inno Setup Projects\pg_installer_setup\pgsql.ico

[messages]
  FinishedLabel=Setup has finished installing PostgreSQL.%nIt should already be running as a serivce.%nYou can verify this by opening the services applet in the control panel.
[CustomMessages]
  ;Service config form
  service_formCaption =Service Configuration
  service_formDescription =The values entered will be used to install the PostgreSQL service
  ;Initdb cluster config form
  init_cluster_formCaption =Database Cluster Configuration
  init_cluster_formDescription =The values entered will be used to initilize the database cluster
  ;Proc selection form
  proc_select_formCaption=Enable procedural languages
  proc_select_formDescription=Select procedural languages to enable in the default database
  ;Default service user account name
  service_name=postgres
  ;Default database super user account name
  su_name=postgres

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]

[Files]
Source: "C:\Documents and Settings\tony\My Documents\Inno Setup Projects\pg_installer_setup\setup_utils.dll"; Flags: dontcopy
Source: "C:\Documents and Settings\tony\My Documents\Inno Setup Projects\pg_installer_setup\small_image.bmp"; Flags: dontcopy

Source: "bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "doc\contrib\*"; DestDir: "{app}\doc\contrib"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion
Source: "lib\libpq.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "share\*"; DestDir: "{app}\share"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "bin\initdb.exe"; DestDir: "{app}\bin"; Flags: ignoreversion ;AfterInstall:init_cluster
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\Configuration Files\Edit postgresql.conf"; Filename: "{app}\data\postgresql.conf";comment: "Edit the main database configuration file (postgresql.conf)"
Name: "{group}\Configuration Files\Edit pg_hba.conf"; Filename: "{app}\data\pg_hba.conf";comment: "Edit the host based authentication configuration file (pg_hba.conf)"
Name: "{group}\Configuration Files\Edit pg_ident.conf"; Filename: "{app}\data\pg_ident.conf";comment: "Edit the ident authentication configuration file (pg_ident.conf)"
Name: "{group}\Command Prompt"; Filename: "{sys}\cmd.exe";Parameters: "/k chcp 1252 && set PGPORT=5432";comment: "Start a Command Prompt in the PostgreSQL Directory"
Name: "{group}\Reload Configuration"; Filename: "{app}\bin\pg_ctl.exe";Parameters: "reload -D ""C:\Program Files\PostgreSQL\8.1\data\""";comment: "Reload the server configuration"
Name: "{group}\Start Service"; Filename: "{sys}\net.exe";Parameters: "start pgsql-8.1";comment: "Start the PostgreSQL service"
Name: "{group}\Stop Service"; Filename: "{sys}\net.exe";Parameters: "stop pgsql-8.1";comment: "Start the PostgreSQL service"

[run]

[Code]
//External DLL functions
#include "external_functions.iss"
const servicename = 'pgsql-8.1';
const service_displayname = 'PostgreSQL Database Server 8.1';
const service_desc = 'PostgreSQL Database Server (postmaster)';
var
    //**** Vars for service config form ****
    service_name_label: TLabel;
    account_name_label: TLabel;
    account_domain_label: TLabel;
    account_password_label: TLabel;
    verify_password_label: TLabel;
    waring_label: TLabel;
    service_name_edit: TEdit;
    account_name_edit: TEdit;
    account_domain_edit: TEdit;
    account_password_edit: TEdit;
    verify_pass_edit: TEdit;

    //**** vars for init cluster form ****
    port_label: TLabel;
    addresses_label: TLabel;
    locale_label: TLabel;
    encoding_label: TLabel;
    su_label: TLabel;
    password_label: TLabel;
    password_verify_label: TLabel;
    data_dir_label: TLabel;
    init_cluster_check: TCheckBox;
    accept_all_connections_check: TCheckBox;
    port_edit: TEdit;
    locale_combo: TComboBox;
    encoding_combo: TComboBox;
    su_edit: TEdit;
    password_edit: TEdit;
    password_verify_edit: TEdit;
    data_dir_edit: TEdit;
    browse_button: TButton;
    set_default_button: TButton;
    //**** vars for proc lang selection form ****
    plpgsql_check: TCheckBox;

    //*** Non form vars ***
    locale_value_list:tstringlist;
    temp_list:tstringlist;
//Code for service routines
#include "service_utils.iss"

procedure init_cluster;
var
   cmd:string;
   service_type:cardinal;
   exepath:string;
   password_list:tstringlist;
   locale_initdb,temp:string;
   selected_index:integer;
   config_path:string;
begin
      if Init_Cluster_Check.checked then
         begin
              selected_index:= locale_combo.itemindex;
              Wizardform.statuslabel.visible:=false;
              locale_initdb:=locale_value_list.strings[selected_index];
              password_list:=tstringlist.create;
              try
                 //add the super user password to a stringlist, save it to disk for use with initdb
                 password_list.add(password_edit.text);
                 password_list.savetofile(ExpandConstant('{app}\password.txt'));
                 WizardForm.FileNameLabel.caption:='Installing PostgreSQL service...';
                 WizardForm.Update;
                 sleep(1000);
                 service_type:= SERVICE_WIN32_OWN_PROCESS;
                 exepath:=ExpandConstant('"{app}\bin\pg_ctl.exe" runservice -N "'+servicename+'" -D "{app}\data\"');
                 installService(exepath,servicename,service_displayname,service_desc,'',service_type,SERVICE_AUTO_START,account_domain_edit.text+''+account_name_edit.text,account_password_edit.text);
                 //Set the top level folder permissions to the service user
                 WizardForm.FileNameLabel.caption:='Setting folder permissions adding '+su_edit.text+ ' with read,write,delete and execute rights';
                 WizardForm.Update;
                 sleep(1000);
                 setfolder_permissions(account_name_edit.text,ExpandConstant('{app}'));
                 WizardForm.FileNameLabel.caption:='Running initdb, this may take a minute, please wait...';
                 WizardForm.Update;
                 //Command line to run as service user
                 cmd:= ExpandConstant('"{app}\bin\initdb.exe" -D "'+data_dir_edit.text+'" -U '+su_edit.text+' --pwfile="'+expandconstant('{app}\password.txt')+'" -E '+Encoding_Combo.text);
                 if locale_initdb = 'none' then
                    locale_initdb:='C';
                 cmd:= cmd+' --locale='+locale_initdb;
                 //runasuser called from external dll
                 runasuser('',account_name_edit.text,account_password_edit.text,cmd);
                 //edit postgresq1.conf and set listen addresses to * if the user has selected this option
                 config_path:=ExpandConstant('{app}\data\postgresql.conf');
                 if accept_all_connections_check.checked then
                 if fileexists(config_path) then
                     begin
                           //reuse the password stringlist
                           password_list.clear;
                           password_list.loadfromfile(config_path);
                           temp:= password_list.text;
                           stringchange(temp,'#listen_addresses = ''localhost''','listen_addresses = ''*''');
                           password_list.text:=temp;
                           password_list.savetofile(config_path);
                     end;
              finally
                   password_list.free;
                   deletefile(ExpandConstant('{app}\password.txt'));
              end;
         end;
      if IsServiceInstalled(servicename) then
        begin
             //start the service
             WizardForm.FileNameLabel.caption:='Starting Service...';
             WizardForm.Update;
             if not startservice(servicename) then
                MsgBox('Service was unable to start, please try manually after the install is finished.', mberror, MB_OK);
             //Create Language plpgsql for template1 and postgres databases
             if Init_Cluster_Check.checked then
                begin
                     if plpgsql_check.checked then
                         begin
                              WizardForm.FileNameLabel.caption:='Enabling selected procedural language(s)';
                              WizardForm.Update;
                              sleep(1000);
                              cmd:= expandconstant('{app}')+'\bin\createlang -U '+su_edit.text+' plpgsql postgres';
                              runasuser('',account_name_edit.text,account_password_edit.text,cmd);
                              cmd:= expandconstant('{app}')+'\bin\createlang -U '+su_edit.text+' plpgsql template1';
                              runasuser('',account_name_edit.text,account_password_edit.text,cmd);
                         end;
                end;
        end;
     //clean up any unneeded files
     WizardForm.FileNameLabel.caption:='Cleaning up...';
     WizardForm.Update;
     sleep(1000);
     DelTree(expandconstant('{app}\share\locale'), True, True, True);
     //clean up the lib dir
     DelTree(expandconstant('{app}\lib\*.a'), False, True, False);
     deletefile(expandconstant('{app}\lib\libpg.dll'));
end;
//Code for custom forms
#include "service_form.iss"
#include "init_cluster_form.iss"
#include "proc_select_form.iss"

procedure CurPageChanged(CurPage: Integer);
var
 imageFile : String;
begin
     imageFile := ExpandConstant('{tmp}\small_image.bmp');
     //if not fileexists(imageFile) then
        ExtractTemporaryFile(ExtractFileName(imageFile));
     WizardForm.WizardSmallBitmapImage.Bitmap.LoadFromFile(imageFile);
end;

procedure InitializeWizard();
var
     lastid:integer;
begin
       locale_value_list:=tstringlist.create;
       temp_list:=tstringlist.create;
       //Create the service config form and show after the select program group form
       lastid:=service_form_CreatePage(wpSelectProgramGroup);
      //Create the init db cluster form show after service form
       lastid:=init_cluster_form_CreatePage(lastid);
       proc_select_form_CreatePage(lastid);
end;

function InitializeUninstall(): Boolean;
begin
     //check if service is installed
     if IsServiceInstalled(servicename) then
        begin
        if IsServiceRunning(servicename) then   //check if service is running
           //stop the service if running
           StopService(servicename);
           sleep(1000);  //sleep for a second to ensure it is fully stopped before removing
           RemoveService(servicename);
        end;
     Result := true;
end;

function InitializeSetup(): boolean;
begin
     if IsServiceInstalled(servicename) then //check if service running
     if IsServiceRunning(servicename) then //stop the service
         Stopservice(servicename);
     result :=true;
end;
procedure DeinitializeSetup();
begin

end;


109 - Read [Files] section from [Code], One function

Author: AW
Article created: 02/04/06 23:25:13
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=109

i needed to do this in one setup, I did see the other article but found it a bit long and big, so made my own.

To make it simplier you need to add [EOF] to the [Files] section.

[Files]
Source: "MyProgram.exe"; DestDir: "{app}"; Flags: dontcopy;
Source: "Setup.iss"; Flags: dontcopy;
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[EOF]

Remeber to include the Setup script file in the [Files] section.

This function will Extract the setup script to {tmp} directory then load and parse it in a TStringList. It will ignore files that dont have a DestDir: param.

function FileList():TStringList;
var
  Tmp:TStringList;
  I, A, P:Integer;
  IsFiles:Boolean;
begin
  IsFiles:= False;
  Tmp:= TStringList.Create;
  Result:= TStringList.Create;
  ExtractTemporaryFile('Setup.iss');
  Tmp.LoadFromFile(ExpandConstant('{tmp}' + 'Setup.iss'));
  DeleteFile(ExpandConstant('{tmp}' + 'Setup.iss'));
  for I:= 0 to Tmp.Count - 1 do begin
    if Pos('[Files]', Tmp[I])  0 then
      IsFiles:= True;
    if Pos('[EOF]', Tmp[I])  0 then begin
      IsFiles:= False;
    end;
    if IsFiles and (Pos('Source:', Tmp[I])  0) and (Pos('DestDir:', Tmp[I])  0) then
      Result.Add(Tmp[I]);
  end;
  for I:= 0 to Result.Count - 1 do begin
    P:= Pos('"', Result[I]);
    if P  0 then begin
      P:= P + 1;
      for A:= P to Length(Result[I]) do begin
        if Result[I][A] = '"' then begin
          Result[I]:= Copy(Result[I], P, A - P);
          Break;
        end;
      end;
    end;
  end;
  Tmp.Free;
end;

procedure InitializeWizard();
var
  I:Integer;
  FL:TStringList;
begin
  FL:= FileList();
  for I:= 0 to FL.Count - 1 do
    MsgBox(FL[I], mbInformation, MB_OK);
  FL.Free;
end;


110 - List of files from the [Files] section into the [Code] section (update)

Article created: 02/04/06 23:30:00
Article URL : http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=110

Michael Winestock wrote:

I have made some changes to the files that you posted on your website and was wondering if you would be willing to update them with the files I have enclosed.
I made the following changes to the files:
    1) Simplified code for better maintenance.
    2) Code now auto allocates space to store the list
of files.  No longer need to manually set a maximum
limit for the number of files.
    3) Replaced code that builds a string containing
the list of files with direct access to the arrays
that hold the list of files.

test.iss

[Setup]
AppName=Test
AppId=Test
AppVerName=Test 1.0
DefaultDirName="C:Test"
DefaultGroupName=Test
OutputBaseFilename=Test Install
OutputDir = "."

[Files]
Source: test.iss; DestDir: {app}

[_EndOfScript]

[Code]
// Always include autoProcessScript.iss after the [Files] section.
#include "autoProcessScript.iss"
#include "setupFileList.iss"

function InitializeSetup() : Boolean;
var
    entry    : Integer;
    listLen  : Integer;
    fileList : TSetupFileList;

begin
    Result   := False;
    fileList := GetSetupFileList();
    listLen  := GetArrayLength(fileList);

    for entry := 0 to listLen - 1 do
    begin
        MsgBox(fileList[entry].Name, mbInformation, MB_OK);
    end;
end;
[/Code]
autoProcessScript.iss

[/Code]
;*****************************************************************************
;
;  File: autoProcessScript.iss
;
;  Description: This file contains the Inno Setup preprocessor code to read
;               out of a Inno Setup script the list of files listed in the
;               [Files] section of a script file.
;
;  Global Routines:
;      InLine
;
;  Internal Only Routines:
;      AllocateFileList
;      Abort
;      DirExist
;      ErrorMessage
;      GetTempDir
;      OutputMessage
;      GetFilenameIndex
;      IsEmpty
;      Trim
;      TrimLeft
;      TrimRight
;      GetTagValue
;      ProcessFilesSection
;      AutoProcessScriptMain
;
;  Notes:
;      This script is processed by the Inno Setup Preprocessor.  It is intended
;      to be included into a Inno Setup script.
;
;  History:
;      02/23/2006 MichaelJW  Removed code that builds a string list of the files
;                            in _AutoProcessScriptMain_.  Removed gm_fileList
;                            and GetSetupFileList macro.  Added InLine and
;                            AllocateFileList macros.
;
;*****************************************************************************

; Define the Carriage Return/Line Feed character.
#pragma parseroption -p-
#define public CRLF "
"
#pragma parseroption -p+

; Define static variables.
#define protected s_ErrorCount  0

; Define global macro variables.
#define public gm_fileListCount -1

#dim public gm_fileListName[1]
#dim public gm_fileListDest[1]
#dim public gm_fileListVersion[1]
#dim public gm_fileListPartNumber[1]

;*****************************************************************************
;
;  Routine: InLine
;
;  Description: Produce inline code.
;
;  Calling Sequence:
;      #expr InLine(line)
;
;  Parameters:
;      line - The line of text to in-line as code.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define public InLine(str line)
    pil_line = line,
    _InLine_

; Define the parameter variables.
#define public pil_line

#sub _InLine_
    #emit pil_line
#endsub

;*****************************************************************************
;
;                     I N T E R N A L   R O U T I N E S
;
;*****************************************************************************

;*****************************************************************************
;
;  Routine: AllocateFileList
;
;  Description: Allocate storage to hold the file list entries.
;
;  Calling Sequence:
;      #expr AllocateFileList()
;
;  Parameters:
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;      The file list storage is allocated based on the number of etries listed
;      in the [Files] section.
;
;  Return Values:
;
;*****************************************************************************

#define AllocateFileList()
    _AllocateFileList_

#sub _AllocateFileList_
    ; Define local variables.
    #define public numEntries

    ; Count up the number of entries listed in the [Files] section.
    #expr numEntries = EntryCount("Files")

    #if (numEntries > 0)
        ; Keep track of how many entries are stored in the file list.
        #expr gm_fileListCount = 0

        ; Allocate storage used to hold info about the install files.
        #dim public gm_fileListName[numEntries]
        #dim public gm_fileListDest[numEntries]
        #dim public gm_fileListVersion[numEntries]
        #dim public gm_fileListPartNumber[numEntries]
    #endif
#endsub

;*****************************************************************************
;
;  Routine: Abort
;
;  Description: Abort the compiler.
;
;  Calling Sequence:
;      #expr Abort(message)
;
;  Parameters:
;      message - Parameter variable containing the error message to show.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;      This routine causes the compiler to abort.
;
;  Return Values:
;
;*****************************************************************************

#define protected Abort(str message = "")
    pa_message = message,
    _Abort_

; Define the parameter variables.
#define protected pa_message

#sub _Abort_
    ; Force the compiler to stop.
    #pragma message CRLF
    #pragma error pa_message + "  Aborting Compile!"
#endsub

;*****************************************************************************
;
;  Routine: DirExist
;
;  Description: Determine if a directory exists.
;
;  Calling Sequence:
;      #expr exist = DirExist(dirname)
;
;  Parameters:
;      dirname - Parameter variable containing the name of the directory.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;      True if the specified directory name exists.
;
;*****************************************************************************

#define protected DirExist(str dirname)
    ((dirname != "") && (FindFirst(dirname, faDirectory) != 0))

;*****************************************************************************
;
;  Routine: ErrorMessage
;
;  Description: Shows an error message.
;
;  Calling Sequence:
;      #expr ErrorMessage(message)
;
;  Parameters:
;      message - Parameter variable containing the error message to show.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected ErrorMessage(str message)
    OutputMessage("******* ERROR: " + message),
    s_errorCount++

;*****************************************************************************
;
;  Routine: GetTempDir
;
;  Description: Returns the temporary directory name.
;
;  Calling Sequence:
;      #expr tmpDir = GetTempDir()
;
;  Parameters:
;
;  Routines Called:
;      DirExist
;
;  Global Variables Referenced:
;
;  Notes:
;      Gets the temporary file path as follows:
;          1. The path specified by the TMP environment variable.
;          2. The path specified by the TEMP environment variable, if TMP is
;             not defined or if TMP specifies a directory that does not exist.
;          3. The current directory, if both TMP and TEMP are not defined or
;             specify nonexistent directories.
;
;  Return Values:
;      String - The fully qualified path of the temporary directory including
;               a trailing backslash.
;
;*****************************************************************************

#define protected GetTempDir()
    _GetTempDir_,
    r_GetTempDir

; Define the return variable.
#define protected r_GetTempDir

#sub _GetTempDir_
    ; Define local variables.
    #define private entry
    #define private tmpDirFound
    #dim    private envPaths[3]

    ; Build the list of environment point to the temp directory.
    #expr envPaths[0] = "TMP"
    #expr envPaths[1] = "TEMP"
    #expr tmpDirFound = False

    ; Attempt to get the temp directory from the user environment.
    #for {entry = 0; (! tmpDirFound) && (envPaths[entry] != ""); entry++}
        r_GetTempDir = GetEnv(envPaths[entry]),
        tmpDirFound  = DirExist(r_GetTempDir)

    #if (! tmpDirFound)
        ; The temp directory is not defined in the user environment so use the
        ; current directory instead.
        #expr r_GetTempDir = "."
    #endif

    ; Make sure the temp directory has a trailing backslash.
    #expr r_GetTempDir = AddBackSlash(r_GetTempDir)
#endsub

;*****************************************************************************
;
;  Routine: OutputMessage
;
;  Description: Outputs a message to the screen.
;
;  Calling Sequence:
;      #expr OutputMessage(message)
;
;  Parameters:
;      message - Parameter variable containing the message to output to the
;                screen.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected OutputMessage(str message)
    pom_message = message,
    _OutputMessage_

; Define the parameter variables.
#define protected pom_message

#sub _OutputMessage_
    #pragma message pom_message
#endsub

;*****************************************************************************
;
;  Routine: GetFilenameIndex
;
;  Description: Returns the index location in the file list based on the
;               filename.
;
;  Calling Sequence:
;      #expr index = GetFilenameIndex(filename)
;
;  Parameters:
;      filename - Parameter variable containing the filename to find.
;
;  Routines Called:
;
;  Global Variables Referenced:
;      gm_fileListCount
;      gm_fileListName
;
;  Notes:
;
;  Return Values:
;      Integer - The index in the file list.
;
;*****************************************************************************

#define protected GetFilenameIndex(str filename)
    pgfi_filename = filename,
    _GetFilenameIndex_,
    r_GetFilenameIndex

; Define the return variable.
#define protected r_GetFilenameIndex

; Define the parameter variables.
#define protected pgfi_filename

#sub _GetFilenameIndex_
    ; Define the local variables
    #define private entry
    #define private found

    ; Search the file list for the passed in filename.
    #expr found = False

    #for {entry = 0; (! found) && (entry < gm_fileListCount); entry++}
        found = (LowerCase(pgfi_filename) == LowerCase(gm_fileListName[entry]))

    ; If the filename was not found in the file list, store filename in the file list.
    #if (! found)
        ; Keep track of how many entries are stored in the file list.
        #expr gm_fileListCount++

        ; Point to where to store the filename.  Store the filename in the file list.
        #expr entry                      = gm_fileListCount
        #expr gm_fileListName[entry - 1] = pgfi_filename
    #endif

    ; Return the index of where the filename resides in the file list.
    #expr r_GetFilenameIndex = entry - 1
#endsub

;*****************************************************************************
;
;  Routine: IsEmpty
;
;  Description: Determines if a line of text is empty.
;
;  Calling Sequence:
;      #expr value = IsEmpty(line)
;
;  Parameters:
;      line - The line of text to check.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     Boolean - True if the line starts with a comment (;) or is blank, False
;               otherwise.
;
;*****************************************************************************

#define protected IsEmpty(str line)
    Local[0] = Copy(line, 1, 1),
    ((Local[0] == '') || (Local[0] == ';'))

;*****************************************************************************
;
;  Routine: Trim
;
;  Description: Strips off all spaces from the left and right side of a string.
;
;  Calling Sequence:
;      #expr trimResult = Trim(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;      trimLeft
;      trimRight
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected Trim(str value)
    Local[0] = TrimLeft(value),
    TrimRight(Local[0])

;*****************************************************************************
;
;  Routine: TrimLeft
;
;  Description: Strips off all spaces from the left side a string.
;
;  Calling Sequence:
;      #expr trimResult = TrimLeft(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected TrimLeft(str value)
    ptl_trimStr = value,
    _TrimLeft_,
    r_TrimLeft

; Define the return variable.
#define protected r_TrimLeft

; Define the parameter variables.
#define protected ptl_trimStr

#sub _TrimLeft_
    ; Define the local variables.
    #define private startAt
    #define private copyLen

    ; Get the length of the string to trim.
    #expr copyLen = Len(ptl_trimStr)

    ; Skip all spaces off the left side of the string.
    #for {startAt = 1;
          (startAt <= copyLen) && (Copy(ptl_trimStr, startAt, 1) == " ");
          1}
        startAt++,
        copyLen--

    ; Actually get rid of any spaces at left side of the string.
    #expr r_TrimLeft = Copy(ptl_trimStr, startAt, copyLen)
#endsub

;*****************************************************************************
;
;  Routine: TrimRight
;
;  Description: Strips off all spaces off the right side of a string.
;
;  Calling Sequence:
;      #expr trimResult = TrimRight(value)
;
;  Parameters:
;      value - The string to trim.
;
;  Routines Called:
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;     String - The resulting trimmed string.
;
;*****************************************************************************

#define protected TrimRight(str value)
    ptr_trimStr = value,
    _TrimRight_,
    r_TrimRight

; Define the return variable.
#define protected r_TrimRight

; Define the parameter variables.
#define protected ptr_trimStr

#sub _TrimRight_
    ; Define the local variables.
    #define private copyLen

    ; Get the length of the string to trim.
    #expr copyLen = Len(ptr_trimStr)

    ; Skip all spaces at the right side of the string.
    #for {1; (copyLen >= 1) && (Copy(ptr_trimStr, copyLen, 1) == " "); 1}
        copyLen--

    ; Actually get rid of any spaces on the right side of the string.
    #expr r_TrimRight = Copy(ptr_trimStr, 1, copyLen)
#endsub

;*****************************************************************************
;
;  Routine: GetTagValue
;
;  Description: Returns the value associated with a tag string from a line of
;               text.
;
;  Calling Sequence:
;      #expr value = GetTagValue(tagStr, line)
;
;  Parameters:
;      tagStr - Parameter variable containing the tag value to find.
;      line   - Parameter variable containing the line of text.
;
;  Routines Called:
;      Trim
;
;  Global Variables Referenced:
;
;  Notes:
;
;  Return Values:
;      String - The value associated with the found tag string.  An empty ("")
;               is returned if the tag string could not be found.
;
;*****************************************************************************

#define protected GetTagValue(str tag, str line)
    pgtv_tagStr  = tag,
    pgtv_lineStr = line,
    _GetTagValue_,
    r_GetTagValue

; Define the return variable.
#define protected r_GetTagValue

; Define the parameter variables.
#define protected pgtv_tagStr
#define protected pgtv_lineStr

#sub _GetTagValue_
    ; Define the local variables.
    #define private tagEnd
    #define private copyLen
    #define private lineStr
    #define private tagStart

    ; Just in case the tag could not be found.
    #expr r_GetTagValue = ""

    ; Find where the next tag value is specified.
    #expr tagStart = Pos(LowerCase(pgtv_tagStr), LowerCase(pgtv_lineStr))

    ; Only care about non-blank lines.
    #if (tagStart > 0)
        ; Shift the line so that the tag value can be processed.
        #expr copyLen = Len(pgtv_lineStr) - tagStart + 1
        #expr lineStr = Copy(pgtv_lineStr, tagStart, copyLen)

        ; Figure out where the tag value starts and ends.
        #expr tagStart = Len(pgtv_tagStr) + 1
        #expr tagEnd   = Pos(";", lineStr)

        #if (tagEnd == 0)
            ; The end of the tag value is not marked so the length is from where
            ; the tag value actually starts until the end of the line.
            #expr tagEnd = Len(lineStr) + 1
        #endif

        ; Copy off the the tag value.
        #expr copyLen       = tagEnd - tagStart
        #expr r_GetTagValue = Copy(lineStr, tagStart, copyLen)

        ; Trim spaces off of the tag value.
        #expr r_GetTagValue = Trim(r_GetTagValue)
    #endif
#endsub

;*****************************************************************************
;
;  Routine: ProcessFilesSection
;
;  Description: Retrieves the filename, destination and version of the entries
;               in the [Files] section of a script file.
;
;  Calling Sequence:
;      #expr ProcessFilesSection(line)
;
;  Parameters:
;      line - Parameter variable containing the line from the script file.
;
;  Routines Called:
;      GetTagValue
;      GetFilenameIndex
;
;  Global Variables Referenced:
;      gm_fileListDest
;      gm_fileListVersion
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected ProcessFilesSection(str line)
    ppfs_lineStr = line,
    _ProcessFilesSection_

; Define the parameter variables.
#define protected ppfs_lineStr

; Define the static variables.
#define protected spfs_inFilesSection False

#sub _ProcessFilesSection_
    ; Define the local variables.
    #define private entry
    #define private sourceDest
    #define private sourceFile
    #define private sourceVersion

    ; Found a new section?
    #if ((Pos("[", ppfs_lineStr) > 0) && (Pos("]", ppfs_lineStr) > 0))
        ; Found the [Files] section?
        #expr spfs_inFilesSection = (Pos("files", LowerCase(ppfs_lineStr)) > 0)

    #elif (spfs_inFilesSection)
        ; Get the next source file name.
        #expr sourceFile = GetTagValue("Source:", ppfs_lineStr)

        ; Get the destination for the source file.
        #expr sourceDest = GetTagValue("DestDir:", ppfs_lineStr)

        ; Skip the entries which don't have a specified destination.
        #if (sourceDest != "")
            ; Retrieve from the install file its encoded version if available.
            ; Some files don't have encoded versions.
            #expr sourceVersion = GetFileVersion(sourceFile)

            ; Only want the basename portion of the filename.
            #expr sourceFile = ExtractFileName(sourceFile)

            ; Get the index for the source file entry in he file list.
            #expr entry = GetFilenameIndex(sourceFile)

            ; Store off the source file destinaton in the file list.
            #expr gm_fileListDest[entry] = sourceDest

            ; Store off the source file encoded version in the file list.
            #expr gm_fileListVersion[entry] = sourceVersion
        #endif
    #endif
#endsub

;*****************************************************************************
;
;  Routine: AutoProcessScriptMain
;
;  Description: Reads and processes lines from a script file.
;
;  Calling Sequence:
;      #expr AutoProcessScriptMain()
;
;  Parameters:
;
;  Routines Called:
;      GetTempDir
;      TrimLeft
;      ProcessFilesSection
;      ErrorMessage
;      OutputMessage
;
;  Global Variables Referenced:
;      gm_fileListName
;      gm_fileListDest
;      gm_fileListVersion
;
;  Notes:
;
;  Return Values:
;
;*****************************************************************************

#define protected AutoProcessScriptMain()
    _AutoProcessScriptMain_

#sub _AutoProcessScriptMain_
    ; Define the local variables.
    #define private entry
    #define private nextLine
    #define private scriptFile
    #define private scriptFilename

    ; The script file will be stored to and read from the tmp directory.
    #expr scriptFilename = GetTempDir() + "_script_.iss"

    ; Save off a copy of the script to the temporary file.  This is done
    ; so that macros can be replaced by the preprocessor before the script is
    ; processed.
    #expr SaveToFile(scriptFilename)

    ; Open the script file to be processed.
    #expr scriptFile = FileOpen(scriptFilename)

    #if (! scriptFile)
        ; Should never happen but just in case...
        #expr ErrorMessage("Unable to open " + scriptFilename)

    #else
        ; Allocate storage to hold the file list etries.
        #expr AllocateFileList()

        ; Process all the lines in the [Files] section of the script file.  Only
        ; want to process those lines that are not commented out or blank.
        #for {1; (! FileEof(scriptFile)); 1}
            nextLine = FileRead(scriptFile),
            nextLine = TrimLeft(nextLine),
            (! IsEmpty(nextLine))
                ? ProcessFilesSection(nextLine)
                : True

        ; Done with the file, close and delete it.
        #expr FileClose(scriptFile)
        #expr DeleteFile(scriptFilename)
    #endif

    ; Indicate the found entries from the [Files] section.
    #for {entry = 0; entry < gm_fileListCount; entry++}
        OutputMessage("Found: " + gm_fileListName[entry] + " (" + gm_fileListVersion[entry] + ")")
#endsub

;*****************************************************************************
;
;                      M A I N   C A L L I N G   P O I N T
;
;*****************************************************************************

#expr OutputMessage("######## Begin Reading List of Setup Files ########")
#expr OutputMessage("")

; Auto call the main entry point.
#expr AutoProcessScriptMain()

#expr OutputMessage("")
#expr OutputMessage("######## End Reading List of Setup Files ########")

#if (s_errorCount > 0)
    #expr OutputMessage("")

    ; Abort the compiler.
    #expr Abort(Str(s_errorCount) + " error(s) found.")
#endif
[Code]
setupFileList.iss

//*****************************************************************************
//
//  File: setupFileList.iss
//
//  Description: This file contains support code to return a list of filenames
//               specified in the [Files] section of the script file.
//
//  Global Routines:
//      GetSetupFileList
//
//  Internal Only Routines:
//      CreateSetupFileList
//
//  Notes:
//
//  History:
//      02/23/2006 MichaelJW  Changed CreateSetupFileList to use in-line code.
//
//*****************************************************************************

type
    // Define a record to hold the informatiom about the files listed in the
    // [Files] section of the script file.
    TSetupFileInfo = record
        Name    : String;    // Name of file.
        DestDir : String;    // The destination path of file.
        Version : String;    // The version of the file.
    end;

    // Define a list of files.
    TSetupFileList = Array of TSetupFileInfo;

var
    // Define static variables.
    s_setupFileList : TSetupFileList;

// Function prototypes.
procedure CreateSetupFileList(); forward;
function  GetSetupFileList() : TSetupFileList; forward;

//*****************************************************************************
//
//  Routine: CreateSetupFileList
//
//  Description: Creates the list of setup files specified in the [Files]
//               section of the script.
//
//  Calling Sequence:
//      CreateSetupFileList();
//
//  Parameters:
//
//  Routines Called:
//      GetFileVersion
//
//  Global Variables Referenced:
//      gm_fileListCount
//      gm_fileListName
//      gm_fileListDest
//      gm_fileListVersion
//      s_setupFileList
//
//  Notes:
//
//  Return Values:
//
//*****************************************************************************

procedure CreateSetupFileList();
begin
    // Initialize the setup file list array.
    SetArrayLength(s_setupFileList, {#gm_fileListCount});

    // Produce in-line code to create s_setupFileList.
    #define private entry
    #define private setupFileListEntry

    #for {entry = 0; entry < gm_fileListCount; entry++}
        /* Generate s_setupFileList[entry] */
        setupFileListEntry = "s_setupFileList[" + Str(entry) + "]",

        /* Produce in-line code to assign each record field of s_setupFileList[entry] */
        InLine(setupFileListEntry + ".Name    := '" + gm_fileListName[entry]    + "';"),
        InLine(setupFileListEntry + ".DestDir := '" + gm_fileListDest[entry]    + "';"),
        InLine(setupFileListEntry + ".Version := '" + gm_fileListVersion[entry] + "';")
end;

//*****************************************************************************
//
//  Routine: GetSetupFileList
//
//  Description: Get the list of setup files, that have versions.
//
//  Calling Sequence:
//      list := GetSetupFileList();
//
//  Parameters:
//
//  Routines Called:
//      CreateSetupFileList
//
//  Global Variables Referenced:
//      s_setupFileList
//
//  Notes:
//
//  Return Values:
//      TSetupFileList - The list of versioned setup files.
//
//*****************************************************************************

function GetSetupFileList() : TSetupFileList;
begin
    // Check to see if the setup file list has been created.
    if (GetArrayLength(s_setupFileList) = 0)
    then begin
        // Setup file list has not been created yet so go and create it first.
        CreateSetupFileList();
    end;

    Result := s_setupFileList;
end;


Web Links - Compilers

Web Links - Main / Compilers

Inno Setup
http://www.jrsoftware.org/isinfo.htm
Inno Setup is a free installer for Windows programs. First introduced in 1997, Inno Setup today rivals and even surpasses many commercial installers in feature set and stability.
Added on: 06 feb 2003

Inno Setup-PreProcessor ISPP
http://yackimoff.cjb.net/
Add-on for Inno Setup and My Inno Setup Extensions. Adds compile time variables to Inno Setup scripts, supports more than #include's.

NOTE: the latest and working ISPP is included in Inno Setup Quick Pack
Added on: 06 feb 2003

Inno Setup Unpacker
http://innounp.sourceforge.net/
Inno Setup is a popular program for making software installations. Unfortunately, it is one way - once made, there is no official method of getting the files from the self-extracting executable other than running it. One piece of software that addresses this issue is InstallExplorer, a plug-in for the FAR Manager that unpacks several types of installations, including those made by some older versions of Inno Setup (IS). It is no longer updated, and it does not work for IS versions 4.x. Innounp is a little different: it is open source and it is based on IS source. Therefore, it is more likely to support future IS versions.
Added on: 05 mar 2004


Web Links - Script Editors-Generators

Web Links - Main / Script Editors-Generators

Tools to help creation and managment of Inno Setup scripts.

ISTool
http://www.istool.org
ISTool is a freeware script editor for Jordan Russell's Inno Setup compiler. ISTool also supports most of the features in Martijn Laan's My Inno Setup Extensions. With this application you can edit these scripts in a visual environment, and creating these setup scripts becomes much easier. This is a very big help for beginners and even for experts.
Added on: 08 set 2003

ISFormDesigner
http://isfd.kaju74.de/
ISFormDesigner helps you creating custom forms for Jordan Russell's Inno Setup. All INNO VCL-Controls are supported and can be placed around the easy-to-use interface. The integrated ObjectInspector (known from Delphi's IDE) allows property changes in a snap.
Added on: 09 set 2003

iwz2iss
http://mikequ.webpark.pl/eng/iwz2iss.htm
iwz2iss converts InstallShield's IWZ project files into InnoSetup's ISS files. The reason to convert (=> switch) from InstallShield to InnoSetup is obvious :-) Converter is able to import most of InstallShield settings. Converts InstallShield's IWZ project files into Inno Setup's ISS files.
Added on: 23 set 2003

InnoScript
http://www.randem.com/innoscript.html
VBP to Inno Setup Script Converter Migrate from Microsoft's Package and Deployment Wizard or just go straight to Inno Setup from a VBP file with InnoScript!!
Added on: 09 gen 2004

InnoSetup Script Component Helper
http://www.riversoftavg.com/downloads.htm#Open%20Source
ISSComponentHelper is a small program for Delphi Component developers who need to distribute their code using InnoSetup. This utility allows you to specify a Delphi project (*.dpr) or Delphi Package (*.dpk), parses the file looking for dependencies, and then generates a list of dependent files that need to be distributed with the project or package. For example, if you had a package called MyPackage.dpk which contained 2 files, File1.pas and File2.pas, ISSComponentHelper would determine you need to include MyPackage.dpk, MyPackage.res, File1.pas and File2.pas (plus any *.res files they need if they include them).
Added on: 20 feb 2004

ScriptMaker
http://www.hisoft2000.de
Scriptmaker is a tool that helps you to construct and maintian installation scripts for Jordan Russel's Inno Setup. this new version of ScriptMaker suported the newest versions of Inno Setup (Jordan Russell, Martijn Laan) and also ISTools (Bjornar Henden). This software is available in different languages. Languages can be choosen by installation or inside the program under a menu item.
Added on: 13 mag 2005


Web Links - Documentation

Web Links - Main / Documentation

Access Runtime Infos about Access Runtime installation, tips, problems, solutions. | Crystal Report
DAO | Microsoft PDW Info about installation tasks and other related to PDW Package and Deployment Wizard

Inno Setup FAQ
http://www.jrsoftware.org/isfaq.php
Inno Setup Frequently Asked Questions
Added on: 06 feb 2003

Microsoft DLL Help Database
http://support.microsoft.com/default.aspx?scid=/servicedesks/fileversion/dllinfo.asp
This database contains information about DLL files that ship with selected Microsoft products. DLL Help exists to assist developers, system administrators, and other IT professionals who face file version conflicts with Microsoft software. Use DLL Help to identify which software installed a specific version of a DLL.
Added on: 06 feb 2003

Essential Pascal
http://www.marcocantu.com/epascal/default.htm
"Essential Pascal" is a free online introduction to the Pascal programming language.
Added on: 06 feb 2003

Inno Setup German helpfile
http://www.hisoft2000.de/En/index.htm
Translated german helpfile for Inno Setup.
Added on: 06 feb 2003

Inno Setup - Assimilation Report
http://is-ar.webhop.org/
For all Inno Setup newsgroup members, especially NEWBIES! This report is a compilation of Inno Setup related information that is especially useful for newbies and should reduce the number of questions that could be easily answered by reading the Inno Setup help file and/or other useful information resources. Also, don't miss to read the list of recommended Third-Party Tools and the recommendation about feature suggestions. Frequent readers should read the revision history at the end of this report.
Added on: 09 set 2003

AppDeploy
http://www.appdeploy.com
AppDeploy.com exists as an independent, unbiased resource for products and information on installation, deployment and automation. By putting all of the information surrounding this subject in one place, a deployment community has grown, and continues to grow. AppDeploy.com features articles, reviews, product summaries, message boards, downloads and more. Eventually every network management team out there comes to the realization that deployment of systems and applications must be more than an afterthought. Just bought 1,000 new computers? Just got the latest version of Microsoft Office? Who knows what to do next? Not doing it smart can cost an endless amount of time, money and frustration. AppDeploy.com is here to help
Added on: 22 set 2003

Einfhrung Inno-Setup
http://www.tutorials.delphi-source.de/inno/
A Getting Started manual from Johannes Trnkle, to use Inno Setup for Delpi projects. In German Language.
Added on: 23 set 2003

Extensive Inno Setup Example Script
http://news.jrsoftware.org/news/innosetup/msg21243.html
by Laurie Comerford Every item of a Inno Setup script is explained very well and clearly.
Added on: 23 set 2003

Inno Setup Knowledge Base
http://www.jrsoftware.org/iskb.php
Inno Setup Knowledge Base contains some hints on how use Inno Setup.
Added on: 21 ott 2003

Windows Application Compatibility Toolkit 3.0
http://www.microsoft.com/downloads/details.aspx?FamilyID=7fc46855-b8a4-46cd-a236-3159970fde94
The Windows Application Compatibility Toolkit (ACT) version 3.0 for Windows 2000 Service Pack 3 or later, Windows XP and Windows Server 2003 contains the tools and documentation you need to design, deploy, and support applications on these platforms. Tools include the latest versions of the Microsoft Windows Application Compatibility Analyzer that simplifies application inventory and compatibility testing, the Windows Application Verifier that assists developers and testers in locating common compatibility issues during the development cycle, and the Compatibility Administrator that provides access to the necessary compatibility fixes to support legacy applications in Windows.
Added on: 11 mar 2004

Inno Setup Dialog files
http://www.mirality.co.nz/inno/isd.php
Introduced with Inno Setup 5 is a new design for custom wizard pages, where each page (including both builtin and custom pages) have a page object, and Inno Setup itself tracks the page's position in the installation. From version 5.0.2 it also supports event handlers defined directly on the page objects.
Added on: 25 ago 2004

Step by Step sample how install an application plus .NET framework
http://www.vbmysql.com/articles/vb_mysql_tutorials/vb_mysql_tutorial-part7.html
There are multiple options for creating installers for a Visual Basic application, including the new one-click installer that will be included with Visual Studio.NET 2005. In this tutorial I am going to introduce you to an excellent Open Source installer called InnoSetup. InnoSetup is a script-driven installer, written in Delphi, that is very versatile and expandable. InnoSetup is free for commercial use and is available for download at www.innosetup.com.
Added on: 15 apr 2005

Inno Setup: Tips & Tricks
http://www.mirality.co.nz/inno/tips.php
Beginner's introduction to Pascal and to [Code]. In its present form, it seems to be some sort of weird hybrid of an FAQ and a tutorial.
Added on: 03 mag 2005

Inoffizielle deutsche Inno Setup Seite
http://innosetup.planet-source.de
Web site in German about Inno Setup, include a forum.
Added on: 09 mag 2005

Einfhrung in Inno-Setup
http://www.dsdt.info/tutorials/inno/
Tutorial in German
Added on: 04 set 2005

Inno Setup Brasil group
http://br.groups.yahoo.com/group/innosetup/
Este grupo tem como objetivo compartilhar informaes sobre a utilizao do Inno Setup e ser um meio de integrao entre os profissionais que utilizam este software
Added on: 22 feb 2006


Web Links - Add-ins

Web Links - Main / Add-ins

ISX Download DLL
http://www.istool.org/isxdl.aspx
This is a very simple DLL that you can use from My Inno Setup Extensions. It allows you to download files from the Internet during the installation process.
Added on: 08 set 2003

AutoIt
http://www.hiddensoft.com/AutoIt/
AutoIt is a simple tool that can simulate key presses, mouse movements and window commands (maximize, minimize, wait for, etc.) in order to automate any windows based task (or even windowed DOS tasks).
Added on: 08 set 2003

SetACL
http://www.helge.mynetcologne.de/setacl/
This is a free commandline utility for setting permissions on virtually anything.
Added on: 09 set 2003

Functions to manage Services
http://www.iaccarino.de/silvio/winstuff.htm
Needs isx 3.0.4 or greater
Examples how to fiddle about win32 functions using isx pascal scripts
- Check, install, remove, stop and start NT based services
- Create or update entries in the services file
- Check version of system components

Added on: 09 set 2003

Inno Key Generator
http://www.mjfreelancing.com/ikg.htm
It enables you to protect the setup of your software with a user specific password feature.
Added on: 09 set 2003

PSVince
http://www13.brinkster.com/vincenzog/psvince.asp
PSVince is a DLL to detect if a module is loaded in memory.
Added on: 09 set 2003

IPUtils
http://isfd.kaju74.de/downloads.html
IPUtils is a simple Winsock DLL wrapper for Inno Setup with the following exported functions: * GetLocalComputerName: PChar; Returns the local computer name * GetLocalIPAddress: PChar; Returns the local IP Address (e. g.: 10.10.10.0) * GetIPAddress(HostName: PChar): PChar; Returns the IP Address of a given HostName
Added on: 10 set 2003

UninsHs
http://www.han-soft.com/uninshs.php
This extension program for Inno Setup allows you to add repair, modify and remove support to your applications with minimal effort. It is very small because use Assemble. Please read the readme file included in the package for further instructions on how to use UninsHs with your own application setups.
Added on: 15 set 2003

Run As User
http://www.palmersoft.co.uk/Runasuser.htm
The function of this software is to allow Administrators to deploy applications that require elevated user rights to users who have restricted rights without having to issue Administrator Credentials to the user. It does this by passing a Run As User type command to the Operating System for the duration of the task at hand. It will only give the elevated rights to that specific task, so you can be safe knowing that the user will not have access to any restricted resources while the application is running.
Added on: 27 mag 2004

H2Reg
http://helpware.net/mshelp2/h2reg.htm
H2Reg.exe is a small utility (177K), that allows you to register MS Help 2.x Collections: Namespaces, Titles, Plug-ins, and Filters without using MSI (Microsoft Installer). Unlike the MS MSI solution, registration using H2Reg is very straight forward. We used the free INNO installer to package the H2Reg download. During installation, Setup registers our H2 document files under the Namespace "hw.H2Reg".
Added on: 29 set 2005


Web Links - Implementation

Web Links - Main / Implementation

GimpWin installer page
http://sourceforge.net/project/showfiles.php?group_id=121075&package_id=132256
Jernej Simoncic uses Inno Setup to create an installer for The Gimp (for Windows). The Gimp is an open source application for photo retouching, image composition, image authoring, etc.
Added on: 09 set 2003

How make a VNC setup with IS
http://www.darkage.co.uk/vnc/legacy/9xdistro.htm
This document gives instructions on how to make your own setup.exe file which will install WinVNC using InnoSetup and ISTool.
Added on: 09 set 2003

Paradox 10 Runtime
http://www.aurigen.com/downloadInstallDemo.html
Here is our inelegant solution for creating a Paradox 10 Runtime
Added on: 09 set 2003

InterBase Installation Info
http://ibinstall.defined.net/dl_scripts.htm
Amongst others you can find here Inno Setup scripts to install InterBase. InterBase is an open source database. It is possible also download a Firebird installation script.
Added on: 09 set 2003

Using Inno Setup 1.3.21 with dB2K Applications
http://www.dbase2000.com/Knowledgebase/int/InnoSetup/InnoSetup_dB2K.htm
A Getting Started manual from Ken Mayer (SQA Engineer), how to create dBase2000 setups.
Added on: 23 set 2003

Creating a Installation Program for Windows
http://guir.berkeley.edu/internal/howto/install_for_windows/
A manual by Jimmy Lin, how to create a setup for Java applications with Inno Setup. Also, a utility is available to check the version of of the currently installed Java Runtime
Added on: 23 set 2003

CAD Programmers Elise Moss
http://www.cadprogrammers.net/EliseMoss.html
Presentation by Elise Moss, how to create a setup for automated installation of custom AutoCAD extensions, like Visual LISP and CAD Menus, using Inno Setup.
Added on: 23 set 2003

Install Visual FoxPro runtimes
http://fox.wikis.com/wc.dll?Wiki~InnoScripts
This is the shortest script that you can use to install a VFP 7 application. It sets up a program called "MyContacts?.exe", including data files, an OCX, and the runtime files for VFP 7. I have purposely left out a lot of options that I normally use to keep this example simple.
Added on: 16 ott 2003

LOGAN s Inno Setup Script Includes
http://members.home.nl/albartus/inno/index.html
In the past adding some custumization to Inno Setup one had to look for pieces of code and implement them into their own script. Now with this new generation of ISSI it s possible to add pre-fab functionality to Inno with just a view lines of code. These includes use the basic include functionality to quickly add and remove various custumization. This is a great advantage to manually incorperating the desired custumization into each script.
Added on: 15 mar 2004

Descriptive Types
http://www.mirality.co.nz/inno.php
What does it do? Well, sometimes your setup contains multiple installation types which you want the user to choose between. And sometimes you just can't convey enough information with just the name of the setup type or the components that it selects. Enter this mod. When included in your setup, it rearranges the controls on the type-component selection screen so that a new descriptive field is added to the right of both the type combo and component list. This field displays the name and lengthier description of the currently-selected setup type. Full instructions on how to use it are included in the file.
Added on: 25 ago 2004

Inno Setup Library distribute Palm applications
http://www.scss.com.au/family/andrew/pdas/palm/innosetup/
This is an Inno Setup script library that allows you to easily create Inno Setup installers to distribute your Palm applications and conduits. It features: Custom wizard page to select which Palm users will install your Palm application. Queues your application for installation on the selected users Palms. Registers your conduit with HotSync and can restart HotSync. Handles de-registration of your conduit. May be used free of charge, even for commercial use. All interfaces are via standard Palm DLLs, no custom EXEs, batch files, or Windows commands are used. Messages grouped in a [CustomMessages] section.
Added on: 14 apr 2005


Web Links - Miscellanea

Web Links - Main / Miscellanea

Pascal Script 3.0
http://www.remobjects.com/page.asp?id={9A30A672-62C8-4131-BA89-EEBBE7E302E6}
Pascal Script is a free scripting engine that allows you to use most of the Object Pascal language within your Delphi projects at runtime. Written completely in Delphi, it's composed of a set of units that can be compiled into your executable, eliminating the need to distribute any external files.
Added on: 09 set 2003


Web Links - Multi Language

Web Links - Main / Multi Language

Translator
http://www2.arnes.si/~sopjsimo/translator.html
This is a tool to help you with translating Inno Setup.
Added on: 09 set 2003

Inno Setup Translations
http://www.jrsoftware.org/files/istrans/
All messages translated in many languages
Added on: 09 set 2003

Bidi Inno Setup
http://www.parmaja.com/projects/innosetup/
Make InnoSetup Forms, Message and controls support Right To Left, depend on Locale info to retrive if the language Right To Left Reading like Arabic , Hebrew....
Added on: 10 mag 2005


Web Links - Windows Installer

Web Links - Main / Windows Installer

Windows Installer MSI files related

MSI2XML / XML2MSI
http://msi2xml.sourceforge.net/
A Windows Installer Database To XML Bi-Directional Converter Features of msi2xml Includes binary streams as MIME (base64) encoded text in the XML file. Alternatively writes binary streams as external files and includes them by references in the XML file. Ensures integrity of binary streams by including a MD5 fingerprint. Includes a default style sheet to display the XML file as a web page in Internet Explorer 5. Sorts rows according to key columns. Version control friendly: time stamps of binary files only change when the file's content changes. Features of xml2msi Instead of including field data in the XML file, an external reference may be specified instead (href). Both, URLs and local paths are resolved dynamically. Accepts an MD5 checksum for all data fields, to ensure data integrity. Automatically sets the correct column types for the standard tables. Validates the data types for standard table columns.
Added on: 09 set 2003

MSI/MSM to InnoSetup and extraction to Access tool
http://www.mitchellscientific.com/smalltalk/CJDMSITool.htm
An easy way to generate an InnoSetup script based on an MSI merge module (an *.MSM file)
Added on: 09 set 2003

MAKEMSI
http://www.labyrinth.net.au/~dbareis/makemsi.htm
MAKEMSI is a freeware tool which allows you to make MSI (or other Windows Installer databases) from scratch within minutes/hours, subsequent rebuilds take minutes and require no MSI skills (in general). The "specification" is a short text file which describes how to build the MSI (or merge module etc). There is extensive error checking to ensure that all errors are detected, the generation step is reliable. The tool encourages reuse, I actually have some 2 line scripts which build complete MSIs! As the build is completely automated (and quick) it could be included as a part of an overnight build process or a projects "make" processing.
Added on: 09 gen 2004

Windows Installer XML (WiX) toolset
http://sourceforge.net/projects/wix
The Windows Installer XML (WiX) is a toolset that builds Windows installation packages from XML source code. The toolset supports a command line environment that developers may integrate into their build processes to build MSI and MSM setup packages.
Added on: 07 apr 2004

msm to msi converter
http://www.ethalone.com/articles/msm2msi.php
There is one disadvantage in the merge modules technology - merge modules cannot be installed on a computer "as is", it is necessary to include it to the Windows Installer installation package (.msi file). Our MSM to MSI Converter eliminates this disadvantage - it can create pure Windows Installer packages from merge modules. Packages created by converter do not require to be included into another installation packages and can be installed on an end-user's computer.
Added on: 23 feb 2005

Visual Studio Installer 1.1
http://msdn.microsoft.com/vstudio/downloads/tools/vsi11/default.aspx
Microsoft Visual Studio Installer 1.1 is the latest update to the graphical tool that simplifies the creation of application setup programs for distribution to single user or enterprise-wide desktops. New with Visual Studio Installer 1.1 is side-by-side installation of versioned DLLs. Setups created with the Visual Studio Installer provide advanced capabilities such as centralized distribution for maintenance and updates, application self-repair, and powerful installation rollback facilities.
Added on: 12 apr 2005

InstallSite
http://www.installsite.org/
InstallSite.org is a collection of tips, technical articles, sample code, tools, discussion forums, and the like for developers creating setups with Microsoft's Windows Installer (MSI) technology or InstallShield products. It also has information for System Administrators and IT Professionals repackaging and deploying MSI packages. InstallSite maintains a list of MSI authoring tools (including commercial tools, freeware and open source) and a list of books about Windows Installer and other setup related topics. There is also an online shop where you can buy sofftware, training, and other services.
Added on: 15 apr 2005


Web Links - Other Installers

Web Links - Main / Other Installers

Free

Installshield
http://www.installshield.com
Installshield
Added on: 16 set 2003

Wise Solutions
http://www.wise.com
Wise Solutions
Added on: 16 set 2003

Ghost Installer
http://www.ginstall.com
Ghost Installer
Added on: 16 set 2003

Setup Factory
http://www.indigorose.com/sf
Setup Factory 6.0 creates compact, bulletproof, professional software installation wizards for deployment via diskette, CD-ROM and Internet via our innovative visual design environment. With dozens of new features including web downloads and action scripting!
Added on: 16 set 2003


Web Links - Free

Web Links - Main / Other Installers / Free

NSIS Installer
http://nsis.sourceforge.net
NSIS, which stands for "Nullsoft Scriptable Install System", is a tool that allows programmers to create such installers. It is released under an open source license and is completely free for any use.
Added on: 16 set 2003

Installer2GO
http://www.dev4pc.com/installer2go.html
Installer2GO is a powerful, easy to use, highly interactive tool for creating reliable Windows Installer packages that meet the Windows 2000/XP logo certification guidelines. Professional developers rely on Installer2GO to quickly and easily create reliable MSI installation packages using the latest Microsoft technologies. Installer2GO is script-free and does not require the user to have any programming background, nor does it require the user to learn any application-specific language! It allows you to use a point-and-click interface to create and manage installation projects based on your business needs. Besides that this tool will allow you to create self-extracting executable files for distribution over the Web, Internet, Intranet and e-mail your data files, graphic images or whatever else you want to distribute!
Added on: 16 set 2003

Setup2GO
http://www.dev4pc.com/setup2go.html
Setup2GO is is a tool for designing full-featured, 32-bit Windows Software Setup solutions in seconds. Setup2GO supports all 32-bit Windows versions in use today - Windows 95, 98, ME, NT4, 2000, XP. Unlike other Installer and Setup creation programs, Setup2GO does not require the user to have any programming background, nor does it require the user to learn any application-specific language.
Added on: 16 set 2003

LizaJet Installer
http://www.lizajet.com/product/freeedition
As Delphi Developer you are of course not satisfied with just another installer; you want an installer that really stands out and has unique advantages over other installers. With the Free Edition you have already that installation tool. With the professional Script Editor, Dialog Editor, Action Designer, split setup, and the comprehensive installation library you can make fully licensed complete installations. This Free Edition has no time limits or other restrictions; you can use it for real production work.
Added on: 23 feb 2004

Ghost Installer
http://www.ethalone.com/products.php
Ghost Installer is a flexible deployment tool, suitable to solve virtually any installation task. Using the new Custom User Interface feature, you can create installation dialogs the way you desire them to be. With Ghost Installer it is possible to perform numerous custom actions and to create scripts that can fulfill almost any software deployment goal. The process of checking registry values and writing configuration files depending on the choices a user makes, is one of many features that give you hands-on control of the installation.
Added on: 13 apr 2005


Web Links - Tools

Web Links - Main / Tools

Resourcehacker
http://www.users.on.net/johnson/resourcehacker/
Resource HackerTM is a freeware utility to view, modify, rename, add, delete and extract resources in 32bit Windows executables and resource files (*.res). It incorporates an internal resource script compiler and decompiler and works on Win95, Win98, WinME, WinNT, Win2000 and WinXP operating systems.
Added on: 03 apr 2003

IASScript
http://www.ath.myweb.nl/IASScript.htm
A tool to create an autorun.inf and beef it up with auto-app start and/or autosetup your application if not installed.
Added on: 09 set 2003

DATViewer
http://www.vincenzo.net/isxkb/modules.php?name=News&file=article&sid=12
DatViewer is a program to see in a more readeable way DAT files created by an installer made with Inno Setup.
Added on: 09 set 2003

InCtrl5 Stay In Control
http://www.pcmag.com/article2/0,4149,9882,00.asp
InCtrl5 is the fifth incarnation of one of PC Magazine's most popular utilities. By monitoring the changes made to your system when you install new software, it enables you to troubleshoot any problems that come up. Virtually every modern program uses an install utility that installs or updates files; these utilities may also record data in the Registry and update INI files or other essential text files. A companion uninstall utility should precisely reverse the effects of the install utility. When a newly installed program causes existing applications to fail, or when the supplied uninstall utility can't complete its task, you need a record of exactly what the original install utility did in order to restore your system. InCtrl5 can provide this record.
Added on: 17 set 2003

Process Explorer Win NT/2K/XP
http://www.sysinternals.com/ntw2k/freeware/procexp.shtml
The Process Explorer display consists of two sub-windows. The top always shows a list of the currently active processes, including the names of their owning accounts, whereas the information displayed in the bottom window depends on the mode that Process Explorer is in: if it is in handle mode youll see the handles that the process selected in the top window has opened; if Process Explorer is in DLL mode youll see the DLLs and memory-mapped files that the process has loaded. Process Explorer also has a powerful search capability that will quickly show you which processes have particular handles opened or DLLs loaded. The unique capabilities of Process Explorer make it useful for tracking down DLL-version problems or handle leaks, and provide insight into the way Windows and applications work.
Added on: 17 set 2003

Dominique Vim Resources
http://www.geocities.com/SiliconValley/Bit/1809/vim/
Here you can find a ISX Syntax file for the VIM editor. The Vim editor is a highly configurable text editor built to enable efficient text editing. It is an improved version of the vi editor distributed with most UNIX systems.
Added on: 23 set 2003

Kixtart Script
http://www.kixtart.org
KiXtart is a logon script processor and enhanced batch scripting language for computers running Windows XP, Windows 2000, Windows NT or Windows 9x in a Windows Networking environment. Package include XNET, a tool to manage (start,stop, install, remove) services.
Added on: 23 set 2003

Dependency Walker
http://www.dependencywalker.com
Dependency Walker is a free utility that scans any 32-bit or 64-bit Windows module (exe, dll, ocx, sys, etc.) and builds a hierarchical tree diagram of all dependent modules. For each module found, it lists all the functions that are exported by that module, and which of those functions are actually being called by other modules. Another view displays the minimum set of required files, along with detailed information about each file including a full path to the file, base address, version numbers, machine type, debug information, and more. Dependency Walker is also very useful for troubleshooting system errors related to loading and executing modules. Dependency Walker detects many common application problems such as missing modules, invalid modules, import/export mismatches, circular dependency errors, mismatched machine types of modules, and module initialization failures.
Added on: 26 set 2003

InstallRite
http://www.epsilonsquared.com/installrite.htm
InstallRite (which includes all the features of InstallWatch) provides "application cloning" as opposed to "disk cloning", simplifying software distribution.
Added on: 03 ott 2003

Install Assistant IA
http://www.installassistant.com
Install Assistant (IA). IA acts as a 'wrapper' for your installation; it handles the updating of any system files your installation may require, and allows you to build a customized 'splash' screen you can display before running setup. It is written in C, and can be run on any Win32 version - Windows 95, 98, Me, NT4, 2000, and XP.
Added on: 03 ott 2003

Fabertoys
http://www.faberbox.com/fabertoys.asp
Faber Toys is a system utility, mainly developed for power users who want to know what's going on in their Personal Computer. It may also be used by programmers as a tool for tracking their applications. The goal is to create an application running on most PC configurations (see system requirements), that use an FPL (Fast Property Load) technology to speed up the recovery of information on slow computers.
Added on: 20 ott 2003

Winspector
http://www.windows-spy.com
What is it Have you ever used Spy++ that comes with MSVC? If you have then you know what Winspector is - but take out all of the annoyances of Spy++ and add in a big bag of handy tweaks and features. Winspector is a Windows application that allows you to look at the messages being sent in the system. When developing your own applications you can see the messages and you can inspect them, but you can't do the same with other running applications. With Winspector you can.
Added on: 21 ott 2003

JanH Killer
http://jan.hlavenka.cz/software.php?lng=1&detail=2
do you need to kill some application before installing new version of your application? JanH Killer is ultimate killing program. you can use it in all versions of Windows and kill more then one program from the command line Win9x/NT/2k/XP small program
Added on: 11 dic 2003

Total Uninstall
http://www.geocities.com/ggmartau/tu.html
Total Uninstall can help you to monitor any changes that were made to your system during installation of a new software product and allow you to perform a complete uninstall without having to rely on the supplied uninstall program (which may leave files or changes behind). To use it, you simply launch the installation program from the Total Uninstall interface and select the system areas to be monitored. The program will then create a snapshot of your system before it installs the new software and an additional snapshot after install completes. It then compares the two snapshots and displays all changes in a graphical tree view, marking all values and/or files that have been added or changed as well as some before/after details. Total Uninstall will save these changes and if you decide to uninstall the application, it will reverse all changes to the previous state.
Added on: 11 feb 2004

UpdateResource
http://www.codedrops.com/content/view/2/25/
UpdateResource, has been released today. It allows changing main icon and version info resource within InnoSetup programs.
Added on: 10 mar 2004

Inno Setup Toolbar for Visual Basic 6.0 IDE
http://www.pscode.com/vb/scripts/ShowCode.asp?txtCodeId=55420&lngWId=1
A fully-functional VB6 IDE Addin for working with the freeware installation program Inno Setup. Tested with Inno Setup Version 3 And Version 4, can autodetect the installation directory and uses the most recent installation. The controls in order are 'Script Editor', 'Script Wizard', 'Compile Script' and 'Configure Toolbar'. The Script Editor will create a default script with the project name in the project folder if one does not exist, and launch the Inno Script Editor.
Added on: 25 ago 2004


Web Links - Microsoft Downloads

Web Links - Main / Microsoft Downloads

Run-times and other stuffs from Microsoft

Jet Engine

 MDAC
Microsoft Data Access Components

Windows 2000

Platform SDK Redistributable: GDI+ RTM
http://www.microsoft.com/downloads/details.aspx?familyid=BFC0B436-9015-43E2-81A3-54938B6F4614
This contains the redistributable files for GDI+.

Added on: 08 ago 2004

Microsoft Layer for Unicode on Windows 95, 98, and Me Systems
http://www.microsoft.com/downloads/details.aspx?familyid=73BA7BD7-ED06-4F0D-80A4-2A7EEAEE17E2&displa
The Microsoft Layer for Unicode on Windows 95, 98, and Me systems (MSLU) helps to provide a layer over the Win32 API on Win9x so that you can write a single Unicode version of your application. This latest version (1.0.4018.0) fixes several bugs reported by customers, including an important bug in string comparison functions. It was first made available on April 24, 2003.
Added on: 08 ago 2004


Web Links - Windows 2000

Web Links - Main / Microsoft Downloads / Windows 2000

Windows 2000 SP4
http://www.microsoft.com/Windows2000/downloads/servicepacks/sp4/download.asp
Windows 2000 SP4 Network and Express
Added on: 07 apr 2004


Web Links - MDAC

Web Links - Main / Microsoft Downloads / MDAC

Microsoft Data Access Components

MDAC 2.5 SP3
http://www.microsoft.com/downloads/details.aspx?familyid=c57692e9-dde7-4a60-b586-5d4fd6794db6
The Microsoft Data Access Components 2.5 SP3 is the same version of the MDAC components installed by Windows 2000 Service Pack 3.
System Requirements Supported Operating Systems: Windows 95, Windows 98, Windows NT DCOM95 required for Windows 95 Windows NT 4.0 Service Pack 3 or later Internet Explorer 3.01 or later
Added on: 07 apr 2004

MDAC 2.6 SP2
http://www.microsoft.com/downloads/details.aspx?FamilyID=8e5f816c-4918-4250-b8bd-3794582c9089
The MDAC 2.6 SP2 redistributable installer installs the same Data Access core components as Microsoft SQL Server 2000 SP2. This release does not include Microsoft Jet, the Microsoft Jet OLE DB Provider, the Desktop Database Drivers ODBC Driver, or the Visual FoxPro ODBC Driver.
System Requirements
Supported Operating Systems: Windows 2000, Windows 98, Windows ME, Windows NT, Windows XP Windows 95 with the Y2K Update and Internet Explorer 4.01 SP2 Windows 98 with the Y2K Update 2 and Internet Explorer 4.01 SP2 Windows NT 4.0 with Service Pack 5 (or later) and Internet Explorer 4.01 SP2
Added on: 07 apr 2004

MDAC 2.7 SP1 Refresh
http://www.microsoft.com/downloads/details.aspx?FamilyID=9ad000f2-cae7-493d-b0f3-ae36c570ade8
The MDAC 2.7 SP1 Refresh release installs the same Data Access core components as Microsoft Windows XP SP1, with the addition of several important bug fixes. The MDAC 2.7 SP1 Refresh release is also included in the Microsoft SQL Server 2000 Service Pack 3a release. This release does not include Microsoft Jet, the Microsoft Jet OLE DB Provider, the Desktop Database Drivers ODBC Driver, or the Visual FoxPro ODBC Driver.
System Requirements
Supported Operating Systems: Windows 2000, Windows 98, Windows ME, Windows NT Available for x86-based computers only
Added on: 07 apr 2004

MDAC 2.8
http://www.microsoft.com/downloads/details.aspx?FamilyID=6c050fe3-c795-4b7d-b037-185d0506396c
Microsoft Data Access Components (MDAC) 2.8 contains core Data Access components such as the Microsoft SQL Server OLE DB provider and ODBC driver. This redistributable installer for the MDAC 2.8 release installs the same Data Access components as Microsoft Windows Server 2003. This release does not include Microsoft Jet, the Microsoft Jet OLE DB Provider or ODBC driver, the Desktop Database ODBC Drivers, or the Visual FoxPro ODBC Driver.
System Requirements
Supported Operating Systems: Windows 2000, Windows 98, Windows ME, Windows NT, Windows XP
Available for x86-based computers only
Added on: 07 apr 2004


Web Links - Access Runtime

Web Links - Main / Documentation / Access Runtime

Infos about Access Runtime installation, tips, problems, solutions.

ODE97: Access 97 Run-Time Application Incorrectly Uses Access 2000 Executable
http://support.microsoft.com/default.aspx?scid=kb;EN-US;248112
When you upgrade a computer from Microsoft Access 97 to Microsoft Access 2000, and an Access 97 run-time application is subsequently installed, you may receive the following error message:
The current user account doesn't have permission to convert or enable this database.
Added on: 15 apr 2004

Access 2000/Access 97 Coexistence FEPs (Frequently Experienced Problems)
http://www.trigeminal.com/usenet/usenet019.asp?1033
List of problems about Coexistence of different version of access runtime
Added on: 15 apr 2004

ACC2000: How to Install Access 97 and Access 2000 on the Same Computer
http://support.microsoft.com/default.aspx?scid=kb;EN-US;241141
Although Microsoft does not recommend it, you can install and use more than one version of Access on a single computer. This article discusses how to install and use Access 97 and Access 2000 on the same computer. This article also discusses some of the problems you may experience when you do this.
Added on: 15 apr 2004

INFO: Avoiding the default system.mdw loss when re-registering Access 97
http://www.trigeminal.com/usenet/usenet018.asp?1033
Microsoft Access, starting in Access 97 and continuing in Access 2000, has a fairly unique design that causes it to re-register itself every it is run when it detects that it is not the "current" version of Access on the machine.
Added on: 15 apr 2004

Access97-Runtime-Script
http://www.unofactura.de/downloads.htm#INNO
Access97-Runtime-Script, page in German.
Added on: 15 apr 2004

script: Access 2000 runtime setup
http://news.jrsoftware.org/news/innosetup.isx/msg00610.html
Access 2000 runtime script.
Added on: 15 apr 2004

Access 97/2000 Runtime-Script
http://news.jrsoftware.org/news/innosetup/msg07993.html
Download link at bottom of page
Added on: 31 mar 2005


Web Links - Jet Engine

Web Links - Main / Microsoft Downloads / Jet Engine

Jet SP8 for Win XP
http://www.microsoft.com/downloads/details.aspx?familyid=2deddec4-350e-4cd0-a12a-d7f70a153156
Microsoft Jet 4.0 Service Pack 8 (SP8) provides the latest updates to the Jet 4.0 database engine with Windows XP, and is discussed in Microsoft Knowledge Base Article KB829558. Download now to get the most current updates for Jet 4.0. After you install this item, you may have to restart your computer.
Added on: 15 apr 2004

Jet SP8 for Win 2000
http://www.microsoft.com/downloads/details.aspx?FamilyID=1E268B39-533E-48B0-B8D7-1781BEFDE1F8
Microsoft Jet 4.0 Service Pack 8 (SP8) provides the latest updates to the Jet 4.0 database engine with Windows 2000, and is discussed in Microsoft Knowledge Base Article 829558. Download now to get the most current updates for Jet 4.0. After you install this item, you may have to restart your computer.
Added on: 15 apr 2004

Jet 4.0 Service Pack 8 [SP8] for Windows 95/98/NT 4.0
http://www.microsoft.com/downloads/details.aspx?FamilyID=4335216C-8CDD-4168-A030-46F815D3F5CD
This download is for Microsoft Jet 4.0 Service Pack 8 (SP8), discussed in Microsoft Knowledge Base Article 829558. Jet SP8 contains updated versions of several files that are utilized by Microsoft Access 2000/2002, Microsoft Office 2000/XP Developer, Microsoft SQL Server, Microsoft Visual Basic, and other Microsoft products that incorporate Microsoft Jet database engine version 4.0.

Note: If you have installed Jet 4.0 SP8 for Access 2000, Office 2000, or another application that uses the Jet 4.0 engine, you do not have to reinstall Jet 4.0 SP8 for Access 2002 or Office XP.
Added on: 15 apr 2004


Web Links - Microsoft Info

Web Links - Main / Microsoft Info

Documents, information and files about files that can or cannot be redist.

Visual Basic

OFF2000: Windows Installer Prompts for Office CD After You Start Program That Uses Mscomctl.ocx File
http://support.microsoft.com/default.aspx?scid=kb;en-us;298385
When you start an application that uses the Mscomctl.ocx file, the Windows Installer may do one of two things, depending on whether you are a member of the Administrators group: If you are logged on as a member of the Administrators group, you are prompted for the Office CD. If you are not a member of the Administrators group, you are not prompted for the Office CD, but you receive the following error message: Error 1706. No valid source could be found for product Microsoft Office 2000 edition. The Windows Installer cannot continue. In the error message, edition is the edition of Office that you installed, for example, Microsoft Office 2000 Premium.
Added on: 29 lug 2004


Web Links - Visual Basic

Web Links - Main / Microsoft Info / Visual Basic

VBRun60sp6.exe installs Visual Basic 6.0 SP6 run-time files
http://support.microsoft.com/default.aspx?scid=kb;EN-US;290887
VBRun60sp6.exe is a self-extracting executable file that installs the latest versions of the Microsoft Visual Basic run-time files that are required by all applications that are created with Visual Basic 6.0. The files include the fixes that are included with Visual Studio 6.0 Service Pack 6.
Added on: 29 lug 2004

INFO: CAB Files Distributed with Visual Basic 6.0
http://support.microsoft.com/default.aspx?scid=kb;en-us;193366
Microsoft provides digitally signed cabinets (.CAB files) on the Internet for download containing core Visual Basic 6.0 files. To download these CAB files, type http://activex.microsoft.com/controls/vb6/ (where is the name of the CAB file that you want to download).
Added on: 29 lug 2004


Web Links - Crystal Report

Web Links - Main / Documentation / Crystal Report

Crystal Reports and Visual Studio .NET - Application Deployment
http://support.businessobjects.com/communityCS/TechnicalPapers/crnet_deployment.pdf.asp
This document discusses how to create an application deployment project to allow you to seamlessly deploy Crystal Report for Visual Studio .NET (this version is installed with Visual Studio .NET) on a client or server computer. This document is for use with Crystal Reports for Visual Studio .NET and later versions.
Added on: 30 lug 2004

Creating an RDC 10 Deployment Package
http://support.businessobjects.com/communityCS/TechnicalPapers/rdc10_deployment.pdf.asp
This document describes the process of creating an RDC deployment package to distribute an RDC application. This document is for use with Crystal Reports 10.
Added on: 31 lug 2004

Crystal Reports 10 RDC Developer Help Files
http://support.businessobjects.com/communityCS/TechnicalPapers/cr10_legacy.zip.asp
This ZIP file contains the Crystal Reports 10 RDC Developer Help Files. The help files include the Crystal Reports Developer help file, the Developer Runtime help file and the Crystal Reports 10 technical reference guide.
Added on: 31 lug 2004


Web Links - Microsoft PDW

Web Links - Main / Documentation / Microsoft PDW

Info about installation tasks and other related to PDW Package and Deployment Wizard

How To Distribute the Microsoft Data Engine (MSDE) With the Package and Deployment Wizard (PDW)
http://support.microsoft.com/default.aspx?scid=kb;EN-US;231923
This article shows how to package and distribute MSDE for Visual Studio using the Package and Deployment Wizard (PDW) tool that ships with Visual Basic 6.0. There are two main steps to perform: Create a Standard Setup Package that redistributes necessary MSDE files to target machines along with your Visual Basic application. The MSDE files will be used on target machines to set up MSDE. This could be done in two ways with the PDW: MSDE manual setup MSDE automatic setup via a Window's Program group icon Use a custom database with MSDE on the target machine.
Added on: 25 lug 2004

INFO: How Setup Wizard and PDW Use Dependency Files
http://support.microsoft.com/default.aspx?scid=kb;EN-US;178354
A dependency (.DEP) file contains information about the run-time requirements of an application or component; it contains information regarding which files are needed, how the files are to be registered, and where they should be installed on the target computer. You can create dependency files for standard Visual Basic projects, ActiveX controls, ActiveX documents, and other ActiveX components. When a component is used in another project, the dependency information for the component is incorporated into the project's dependency information. This dependency information is used by the Setup Wizard (known as the Package and Deployment Wizard or PDW in Visual Basic 6.0). When you use the Setup Wizard or PDW to generate a setup for your project, the wizard refers to the project's dependency information to determine which files should be distributed with your project and how those files should be installed. All of the ActiveX controls that ship with Visual Basic have a companion dependency file. It is strongly recommended that you generate a dependency file for your component, especially if you intend that the component be used in other Visual Basic projects. The Setup Wizard and PDW give you the option to generate a dependency file for your project or component. The dependency file may also be generated using a text editor if you cannot use the Setup Wizard or PDW to create one. The remainder of this article describes the syntax for a dependency file.
Added on: 25 lug 2004

PRB: Shdocvw.dll Is Not Included in PDW Setup Package
http://support.microsoft.com/default.aspx?scid=kb;EN-US;191692
When you use the Package and Deployment Wizard (PDW) to create a setup package for a Visual Basic project that contains the Microsoft Internet Controls, PDW does not include the Shdocvw.dll file in your setup package.
Added on: 25 lug 2004

Visual Basic "Help" Resources PDW
http://reliableanswers.com/vb/help.asp
Visual Basic "Help" Resources PDW
Added on: 25 lug 2004

INFO: Support for Third-Party Installers
http://support.microsoft.com/default.aspx?scid=kb;en-us;257773
The following installation tools are supported by Microsoft for the distribution of Visual Basic applications: Package and Deployment Wizard (PDW) - Visual Basic 6.0 only Visual Basic Application Setup Wizard - Visual Basic 4.0 and 5.0 only Windows/Visual Studio Installer - Visual Basic 6.0 only Support for other installation programs is provided by the vendor of the product. However, some information must be provided to an installer program in order to ensure a successful installation regardless of the tool that is used. The installer needs to know which files need to be distributed, and that only those files are distributed. The purpose of this article is to explain the recommended method of gathering this information.
Added on: 26 lug 2004

INF: VB .DEP Files Not Available for SQL Server Redistributable Components
http://support.microsoft.com/default.aspx?scid=kb;en-us;228940
Currently there are no dependency (.dep) files shipped with SQL Server for the redistributable components (for example, Dtspkg.dll, Dtspump.dll, and so forth). If a customer is building and deploying Visual Basic applications using SQL Server redistributable components, the dependency files have to be determined using utilities such as Dependency Walker. These files must then be added manually in the Visual Basic deployment wizard. The file, Redist.txt, in the root directory of the SQL Server installation CD has more information on the redistributable components that ship with SQL Server.
Added on: 26 lug 2004

INFO: Useful Tools for Package and Deployment Issues
http://support.microsoft.com/default.aspx?scid=kb;EN-US;198038
Microsoft Visual Basic ships with the Package and Deployment Wizard (PDW), a tool designed to aid developers in packaging and deploying their Microsoft Visual Basic applications. Packages created with the PDW use compressed cabinet files (.cab files) for distribution. This article discusses several utilities that may be used to modify, view or manipulate cabinet files. As well, during the distribution process, problems may arise when deploying applications to various environments or when testing. This article also discusses some tools that may be useful for troubleshooting application deployment issues.
Added on: 26 lug 2004

INFO: Description of Setup.lst Sections
http://support.microsoft.com/default.aspx?scid=kb;EN-US;189743
The Package and Deployment Wizard (PDW) uses the information located in the Setup.lst file to install applications. This file contains all relevant information for the application to be installed. This article describes the usage of each section in the Setup.lst file.
Added on: 26 lug 2004

How To Package and Deployment Wizard Installation Macros
http://support.microsoft.com/default.aspx?scid=kb;EN-US;189739
Installation macros can be used throughout the steps of the Package and Deployment Wizard (PDW) to install files to specific locations. This article documents the available installation macros and what the macros will equate to during the installation process.
Added on: 26 lug 2004

Get Started with the Package and Deployment Wizard (PDW)
http://support.microsoft.com/default.aspx?scid=/support/vbasic/gettingstarted/pdw.asp
The Visual Basic Package and Deployment Wizard (PDW) automates many of the steps that are involved in the distribution of applications. The PDW helps you create Windows setup or cabinet (.cab) files for your application, group them into a unit, or package, that contains all information needed for installation, and deliver those packages to end users. You can use the PDW to create packages that are distributed on floppy disks, CDs, a local or a network drive, or the Web.
Added on: 29 lug 2004


Web Links - DAO

Web Links - Main / Documentation / DAO

How To Redistribute DAO 3.6
http://support.microsoft.com/kb/q233002/
DAO version 3.6 is required for applications using DAO that need to read and write to Access 2000 .mdb database files.
Added on: 08 apr 2005

How To Redistributing DAO with Your Visual C++ 5.0 Application
http://support.microsoft.com/kb/q167523/
There are 2 approaches to redistributing DAO 3.5 with your Visual C++ 5.0 application: You can redistribute the DAO Setup located in the VCREDISTDAO directory. -or- You can follow the steps in the Daosetup.rtf installed with the DAO SDK. Note that the information in the "Installing DAO on Another Machine" topic for the DAO SDK is incorrect and the information should be ignored.
Added on: 08 apr 2005

How to install DAO 3.5 & DAO 3.6 manually
http://www.islamov.com/diamond/q&a.html#daoinstall36
How to installa DAO 3.5 & DAO 3.6 manually from freqeuntly asked question about Diamando Software.
Added on: 08 apr 2005