![]()  | 
        
           
 
 Printable version of this article ForewordFirstly, please note that I'm just another user of Inno Setup, not one of its developers. To the best of my knowledge, information contained in this article was correct at the time it was written, but it's possible that it contains errors, or that an upgrade to Inno has rendered something mentioned here unnecessary or incorrect. This is intended as an aide, rather than anything definitive :) PrerequistitesI'm expecting you to be using Inno Setup 5. Some of the material covered here is also applicable to IS4, but for most of the cooler stuff you need IS5. In fact, some of the material is applicable to any sort of programming, not only Inno Setup scripts. A lot of general principles are still relevant. I'm also expecting you to be reasonably familiar with programming, if not Pascal specifically. This isn't a programming tutorial -- while I will be covering some of the syntax of Pascal, I won't cover much of the semantics -- you're expected to have some basic programming knowledge already :-) 
 Now let's get down to it. Contents
 [Code] basics: readabilityThe most important thing to bear in mind at all times is that your code needs to be readily understandable. This is true for all programming, incidentally -- not just scripting. Don't forget that your code may need to be read, understood, and maintained by someone else (especially for a commercial or other team project) -- and even if you're working on something completely private, you'll need to be able to come back after three years (having completely forgotten why you were doing certain things) and be able to easily pick up where you left off. There are a number of techniques that enhance code readability. I'm not going to go into too much detail here (there are plenty of books on the subject already), but I'll mention some of the ones that I think are the most important: 
 [Code] basics: global variablesLike I said, this isn't really a Pascal tutorial, but I'll cover some of the basics. Global variables are considered a "code smell" -- ie. something that makes your code smell bad, and thus should be used as little as possible. Since ROPS (Inno's scripting language) doesn't support classes, however, you'll find that to do anything nontrivial, a certain amount of global variables are required. You should try to minimise the number of globals you use, but don't do so at the expense of the readability of your code. 
	To declare a global variable, just put a  
 
	Globals are typically declared at the top of your program (ie. at the top of
	your  [Code] basics: commentsThe [Code] section uses Pascal-style comments, which are quite different from Inno's standard leading-semicolon. In fact, the leading-semicolon form will not work inside the [Code] section, just as the Pascal comments don't work outside of [Code]. So be careful. 
	So what are the Pascal comments?  There are actually two types.  One is
	the familiar single-line comment, which starts at the comment marker and
	continues until the end of the line.  This is written using two forward
	slashes ( 
	It's important to realise that if you start a block comment, you must end it 
	with the matching terminator.  You can't stop a  [Code] basics: strings
	I won't go into too much detail here -- everybody knows what strings are.
	But it's worthwhile mentioning a few details.  First: strings are kept
	inside single quotes ( 
	Hashed characters can be concatenated with each other and with quoted strings
	without having to use the  
	There's one more thing you need to watch out for.  Strings, unlike arrays
	(which we'll cover later), use a one-based index.  This means that if you
	have a string variable  Arithmetic expressions
	This is all pretty standard, and should be familiar to anyone with
	programming experience.  The one thing to watch out for is that in Pascal,
	the slash operator ( [Code] basics: defining constants
	Not to be confused with Inno Setup constants (such as  
 [Code] basics: defining enumerations
	I'm sure we all know that a  
 The above snippet illustrates a Pascal convention: giving each alterative a prefix from the initials of its parent type or variable. The reason for doing this is because these values all share the same namespace with each other (and with other constants), and so you can get a clash quite quickly. [Code] basics: defining typesWhen working with enumerations (above) and arrays (below) in particular, you can get quite complicated constructs for your variable types. You need to make sure that you only specify them once. There are two reasons for this: firstly, to avoid duplication (rule 3), and secondly, to avoid incompatibility. To demonstrate that, have a look at this: 
 
	The above code will not compile, even though it may seem reasonable at first 
	glance.  You'll get a Type Mismatch error on the line that calls 
	 
 
	Note: standard Delphi convention (shared by some flavours of Pascal,
	including Inno Setup's) is to prefix a type name with "T".  Following
	this convention,  [Code] basics: arrays
	There are times when you need to work with lists of things.  Inno already
	provides direct support for lists of strings (either through  
 
 | ||||||||||||||||||||||||||
		if condition then begin // ... end; if condition then begin // ... end else if condition then begin // ... end else begin // ... end;  | 
	Note that semicolons are forbidden immediately before an else;
	this is because in Pascal semicolons are used to separate statements, and
	the else is considered part of the same statement as the
	if.  Stick to the begin/end rules that
	I've already outlined and you should be fine.  (This is also one of the
	reasons why I keep begin and end on the same line
	as the if statement -- things read better when you get to the
	else, and writing it like this actually reinforces the fact that
	you shouldn't put a semicolon on the end just before the
	else.
	Now onto the conditions themselves.  A valid condition is simply any
	expression with Boolean type.  Which could be simply calling
	a function that returns a Boolean, or it could be using a
	relational operator, or even some combination of both.
	The relational operators are fairly straightforward: = tests
	for equality, <> for inequality, and then you've got
	less-than, greater-than, etc, as you'd expect.  Note that in Pascal
	= always represents an equality test, unlike in some other
	languages -- assignment uses the := statement instead, which
	cannot be used inside an expression anyway.
	The boolean operators are and, or, and
	not.  (There's also an xor operator, but that's
	seldom useful.)  These are fairly self-explanatory, but there is one
	important thing you need to remember: they have quite high precedence.
	This means that a statement like this:
	  if a = 5
		or a = 6 then begin
	.. will not do what you'd naturally expect.  Since the and
	has higher precedence, the above is equivalent to this:
	
	  if a = (5
		or a) = 6 then begin
	.. which will end up doing some quite strange bitwise operations and not
	at all what you thought it would.  The solution is to explicitly bracket
	everything else around the boolean operators, like so:
	  if (a = 5)
		or (a = 6) then begin
	See True, false, or maybe below
	for another bit of potentially unexpected behaviour.
	There's one other type of conditional block in Pascal, and that's the
	case statement.  It's similar to switch in C and
	Select Case in VB -- it jumps to one particular block of
	statements depending on the value of a single variable.  That variable must
	be an ordinal type (integer, character, or enumeration) -- you can't use
	strings or floating-point numbers, as with constants.  And the selectors
	cannot be variables, they must be constant.
		case variable of option1: begin // ... end; option2: begin // ... end; option3: begin // ... end; else begin // ... end; end;  | 
	There are a few things worth pointing out here.  Note that I've changed my
	standard indentation pattern a little.  This is because it's better to
	preserve code readability than to stick to rigid style rules, and it just so
	happens that the above style is (I think) the clearest and most readable way
	to structure this particular statement.  Note the final else and
	end -- they don't have a corresponding begin.
	That's because they relate directly back to the case statement
	itself.  The else block is executed if none of the other
	selectors match.  (It's optional, but it's usually worthwhile to include,
	even if for no other reason than to display an error dialog saying "Oops,
	the developer forgot to handle something.  Tell them what an idiot they
	are." :-)
	In addition, you are not limited to single values for those selector options.
	You may also specify comma-separated values (eg. 4, 6, 129),
	ranges of values (eg. 4..79), and of course combinations of the
	two.  (You can't "fall through" one handler block to another, the way you
	can in C.)
It's fairly common to want to do things repetitively, and this is where loops come in. Once again, looping structures should be familiar to anyone who is used to programming, so I won't explain in detail. What you may not be familiar with is the specific syntax used in Pascal for these structures, and so that's what I'll cover here:
		// A for loop, with increasing index: for variable := low to high do begin // ... end; // A for loop, with decreasing index: for variable := high downto low do begin // ... end; // Do something while the condition is true: while condition do begin // ... end; // Repeat something until the condition is true: repeat // ... until condition;  | 
	There are four points worth noting about these.  First, Pascal does not
	support "steps" in the for loop.  The index variable you
	specify will either increase by one or decrease by one each time -- you
	can't make it increase by two each time, for example, although you can
	accomplish the same effect through multiplication within the loop body.
	Second, the variable in for loops is limited to integers in
	ROPS.  (In regular Pascal, you can also use characters and enumeration
	constants, just like with arrays.)
	Third, the while loop will be skipped entirely if the condition
	is false the first time.  The repeat loop will always execute at
	least once.  This should be pretty much as you'd expect, given the placement
	of the conditions within the loop structure.
	Finally, note that the repeat...until loop does
	not use the begin or end keywords.  Despite this,
	it still permits you use multiple statements within the loop.
	Normally, a routine ends when execution "falls off" the end.  At that point, 
	control returns to the calling routine (or to Inno, for an event function). 
	However, sometimes you want to leave the routine earlier.  The most common 
	case is returning early from a function.  Pascal doesn't have an equivalent to 
	C's return x; statement (which returns a specific value). 
	Instead, whatever value is stored in the Result variable when 
	control leaves the function is the value that's returned.
	A few useful points about the Result variable: this is an implied 
	variable.  You don't need to explicitly declare it -- it's automatically 
	created based on the function's definition.  It is always the same type as the 
	function itself.  In all other respects, however, it is a normal variable.
	You can assign and reassign it as much as you want, use it in comparisons,
	whatever.  But whatever it holds when the function returns is what gets
	returned to the caller.
	So, now we know about Result.  A fairly common coding style is
	to test for preconditions near the start of your routine, and simply bail out
	(with some appropriate return value) if conditions aren't satisfactory.  For
	example, in your InitializeSetup function you may want to check
	that a particular third-party component is already installed.  If it isn't,
	then there isn't really much point in continuing.  Now, you could do this
	with nested if statements -- but that quickly gets awful.  Have
	a look at this:
| BAD CODE | 
|---|
function InitializeSetup(): Boolean; begin Result := False; if Component1IsInstalled() then begin if Component2IsInstalled() then begin if Component3IsInstalled() then begin Result := True; // ... end else begin MsgBox('Sorry, no component 3.', mbError, MB_OK); end; end else begin MsgBox('Sorry, no component 2.', mbError, MB_OK); end; end else begin MsgBox('Sorry, no component 1.', mbError, MB_OK); end; end;  | 
  Is this good code?  No.  All those nested if statements are
  messy -- and what's more, they violate the proximity guideline: the error
  message telling the user that component 1 couldn't be found is nowhere near
  the code that does the checking.  So, let's write it a different way.  The
  next approach is sometimes referred to as "exit early" -- and that's exactly
  what we're going to do, with the help of the exit keyword:
function InitializeSetup(): Boolean; begin Result := False; if not Component1IsInstalled() then begin MsgBox('Sorry, no component 1.', mbError, MB_OK); exit; end; if not Component2IsInstalled() then begin MsgBox('Sorry, no component 2.', mbError, MB_OK); exit; end; if not Component3IsInstalled() then begin MsgBox('Sorry, no component 3.', mbError, MB_OK); exit; end; Result := True; // ... end;  | 
  Doesn't that look better?  Another way of writing it would have the 
  Result := False; line removed from the start and instead written 
  immediately before each of the exit; lines.  This makes it look a 
  lot more like the C return statement.  Of course, doing that in 
  this case would lead to a bit of duplication, but it's not an especially bad 
  form, so go ahead and do it that way if you feel more comfortable with it.
	So, exit will exit from the routine.  What if you don't want 
	something that drastic?  What if you just want to break out of a 
	for, while, or repeat loop?  The 
	question brings the answer: it's the break keyword.  Similarly, 
	if you want to abandon the current iteration of the loop and jump right back 
	to the increment/condition, you use the continue keyword.
withI'm including this section just so people can recognise this particular language construct and understand what it actually means. In my opinion, however, this statement is the Spawn of EvilTM and should not be used under any circumstances.
| BAD CODE | 
|---|
		MyPage := CreateInputOptionPage(wpSelectComponents, 'a', 'b', 'c', True, False); with MyPage do begin Add('1'); Add('2'); SelectedValueIndex := 1; end;  | 
	So, what does all that mean?  Within a with block, you can
	refer to methods and properties of a specific variable without having to
	specify the variable name each time.  In the above snippet, for example, the
	first line is actually calling MyPage.Add('1');, and the third
	is equivalent to MyPage.SelectedValueIndex := 1;.
	"What's wrong with that?", I hear you ask.  The problem is ambiguity.
	Look again.  If you hadn't noticed that with statement, or knew
	how it worked, you'd think that this is just calling a normal procedure and
	assigning to a regular variable.  So using with has actually
	made it harder to understand this code, which is of course a step in
	the wrong direction.
	It gets worse.  Imagine that the with block above was in a
	procedure somewhere, and took a parameter called Name, which we
	want to add as a third option.  So let's insert a new line just below the two
	existing Adds, right?  Something like this:
	  Add(Name);
	But that won't work.  You see, MyPage also has a property called
	Name.  And because the with block is closer than the
	parameter declaration, the property takes precedence -- so you're actually
	calling MyPage.Add(MyPage.Name);.  And there is in fact no
	way to access the parameter from within the with block -- it's
	completely hidden.
Ok, so we'll just look up all the properties and methods, and then make sure we pick a unique name. That's safe, right? Wrong. Inno is an evolving product, which means that new properties and methods are getting added to objects all the time. So what may be safe today, could be unsafe tomorrow -- and suddenly your code stops working for no readily apparent reason. That sort of problem is very difficult to spot -- especially if the code itself is only executed under certain conditions.
	Given all that, why would people use with?  It only provides a
	single benefit: less typing.  It's frequently introduced when people want to
	manipulate several properties on an object buried several levels deep, for
	example if you wanted to mess with the properties of
	WizardForm.NextButton.Parent.  But there's a much safer way to
	do that -- just declare a local variable with a nice short name.  Then you
	can assign the object to it using its long-winded name and thereafter just
	use the short name for everything.
	The moral of the story: don't use with.  Ever.  It provides no
	benefits and comes with significant dangers.
	(For the curious: I have less objection to VB's With statement.
	The difference is that in VB's With, you have to use a leading
	dot when referring to a property or method on the named variable, which
	eliminates the ambiguity problem.  Until you start nesting with
	statements, of course.  (shudder))
Ah, boolean logic. If you've made it this far then I'd hope that you already know how boolean logic works in general -- you did read the Conditional statements section, didn't you?
	When writing conditional logic, some people like to be very explicit,
	writing such things as:
	if Test = True then ...
	You may have also noticed that I never do that.  There's two very good
	reasons for that:
True is of course 
		when it is already a Boolean.  And of course all the if 
		statement wants is a Boolean. You've already got what it wants, so why go do 
		an unnecessary comparison? Aha!  But what about testing if it's equal to 
		False?  Well, you could do that, but it's tidier just to use 
		not instead (eg. if not Test then).True or False, right? 
		Wrong.  Booleans are internally stored as bytes, which can of course contain 
		any number from 0 through to 255. Normally, False is encoded as 
		0, and True as 1.  But sometimes things can get confused 
		(especially when you're interacting with external code which has possibly 
		been written in a different language, which might have different definitions 
		for True and False [for example, VB defines 
		True as 255, not 1]), and you get some other value 
		instead.
	That last one deserves a bit more explanation.  Fortunately, in logic contexts 
	(for example, in the if statement or when using the boolean 
	operators and and or) Pascal has a rule which says 
	"if it's zero, it's False, and if it's not False, 
	then it's True".  So if you somehow get a "17" in there, then 
	your code will still work if you just use if x then or if 
	not x then.  But if you had said if x = True then... well, 
	True is 1, and 17 isn't 1 now, is it?  So you've gotten yourself 
	into a weird situation where the variable is True but is 
	not equal to True.
	
 
	Comparing against False is not as dangerous.  Just about 
	everybody agrees that False is 0, and 0 is the only value that 
	can be False. So if you really must use a comparison, you 
	can fairly safely do comparisons in terms of False (eg. if 
	x = False then, if x <> False then).  But really, it's 
	much cleaner if you just leave out the comparison altogether.
	So, in summary: don't ever compare anything with True!
In Pascal, you must declare a routine before it can be called. Sometimes that just isn't convenient, especially when you're trying to keep related functions together. This is where forward references come in handy:
procedure Routine2(); forward; procedure Routine1(); begin // ... Routine2(); // ... end; procedure Routine2(); begin // ... end;  | 
ExpandConstant
	You'll frequently need to use Inno constants from within your [Code].  To do
	this, you need to use the ExpandConstant support function.  Why?
	Because the actual value of those constants isn't actually known until
	installation time (and sometimes not even then -- {app} for
	example isn't known until after the wpSelectDir page).  This
	means that the compiler can't do anything with them, and you need to call
	a function to expand them at runtime.
	Using ExpandConstant is easy.  Here's an example:
	if FileExists(ExpandConstant('{app}\readme.txt')) then ... --
	this checks to see if the readme.txt file exists in the
	application's folder.  Note that you can put other text in there as well,
	not only the constant itself.  This is in fact recommended, since Inno
	will automatically compensate and generate a valid path, whether
	{app} already has a trailing backslash or not.
	ISPP variable "constants" (such as {#SomeVariable}) are an 
	exception to this rule.  You do not need to use ExpandConstant on 
	them (unless of course the ISPP variable expands to contain {app} 
	or some other constant).  This is because unlike the Inno constants, the 
	actual value of the ISPP constants is known at compile time.
	Sometimes, to decide whether you should install a particular file or run a
	particular subinstall task, you just need to use a more complicated condition
	than that permitted by [Components], [Tasks], 
	Flags, or Windows versions.  That's when it's time to use a
	Check function.  Again, these are covered pretty well in the
	helpfile, so I'll be brief.  Check functions must be
	functions, and must return a Boolean.  Normally they won't take any
	parameters, but you can specify some if you want to.
Another point worth mentioning is that the Check parameter itself is considered outside of the Pascal "scope" rules, meaning that you do not need to define a forward reference for it, or to make sure your [Code] section comes before the others, or anything like that.
Now here comes the fun part. Check functions are not guaranteed to be called, nor are they guaranteed to be called only once. That's not quite as bad as it sounds, and when you think about it you will see why it's that way. Inno will call the Check function whenever it thinks, to the best of its knowledge, that the entry in question should be used. The Check function acts as a sort of tiebreak, in effect. If Inno has already decided that the entry shouldn't be installed, then there's no point in calling the Check function -- nothing it can do would change Inno's mind. If Inno thinks that it should be installed, however, it then gives the Check function a chance to veto it. And for some types of entries, Inno may need to ask the question again -- and if it does, then it will have to call your function again. Why? Simply because Inno has no idea what you're actually checking for. For all it knows, conditions might have changed in the interim, and while it shouldn't have been installed before, now it should be. The upshot of all of this is that you need to make sure that your Check functions simply answer the question, without any other side effects.
  For most types of Check functions (those on [Files] or
  [Registry] entries for example), Inno will wait until it's
  performing the actual installation before asking.  This means that you'll be
  able to use custom wizard pages to make your decisions, among other things.
  For some types, however (such as those on [Components]), Inno
  will ask the question almost immediately, before any wizard pages
  have been shown.  That's because of when Inno fills out all the pages -- just
  before it calls InitializeWizard, in fact.  So for this type of
  Check function you won't be able to use custom pages to help make your
  decisions.  If you're in doubt about when a particular Check function is
  getting called, simply put a MsgBox into it.  That'll quickly show you
  exactly when and how often it's getting called -- although bear in mind that
  a different version of Inno may end up calling it differently.
As mentioned above, Inno may call your Check functions more than once, just in case your conditions change. But what if your conditions don't change? What if your test is particularly lengthy, or won't work quite the same if you do it a second time? There are a couple of ways to resolve this:
InitializeSetup or in CurStepChanged (see
		Hooking into the start or end of the installation).
		Store the result into a global variable.  In your Check function, simply
		return that variable.var DotNet11Installed: (dnUnknown, dnNotInstalled, dnInstalled); function IsDotNet11Installed(): Boolean; begin; if DotNet11Installed = dnUnknown then begin DotNet11Installed := dnNotInstalled; if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322') then DotNet11Installed := dnInstalled; end; Result := DotNet11Installed = dnInstalled; end; function InitializeSetup(): Boolean; begin; DotNet11Installed := dnUnknown; Result := True; end;  | 
RegKeyExists may not be the most complex test in the
		world, and doesn't really need this sort of protection.  But the
		principle is sound.
	One final note, before I leave the subject of Check functions:  Check
	functions are only called at install time, never
	at uninstall time -- even if they're placed on an [UninstallRun]
	entry.  This is intended to cover situations where, for example, you have a
	Check function that decides there's no need to install a particular
	subcomponent.  Similarly, you can place the Check function on the
	[UninstallRun] entry as well to indicate to it that there's no
	need to run its uninstaller.  Note that due to the way that Inno's uninstall
	logs work, if the installer is run multiple times (perhaps when upgrading to
	several successive versions), then the entry will be executed if the Check
	function returned True on any of those times.
	(If you forgot to add the RunOnceId, then it may even get called
	multiple times!)
One common mistake is to think that Check functions are suitable for asking the user a question at uninstall time, such as whether to delete a set of data files or not. They're not suitable for this, as outlined above. If you want to do that, you'll have to do it using pure [Code], not with Check functions.
BeforeInstall and
	AfterInstall parameters
	Almost all "action" entries (such as [Files] and [Registry]) can be given a
	BeforeInstall or AfterInstall handler (or even
	both!).  Again, the help file is pretty clear on how these work.  But again,
	I'll give a brief intro.
	Either routine must be a procedure.  They may take any number of parameters,
	though typically you won't need any.  And just like Check functions, they will
	not be called if Inno decides (for whatever reason) that the entry in
	question should not be installed.  Unlike Check functions, however, they are
	guaranteed to be called exactly once (if at all), and if the BeforeInstall
	routine is called, then the AfterInstall routine will be as well
	(unless the installation failed and aborted on that particular entry).
	These are handy for performing minor tasks relating to a particular file.  For
	example, you may put an AfterInstall handler on a configuration
	[File] to first copy in a basic template file, and then update it
	based on the user's choices in the wizard.
	You'll sometimes need to write some complicated logic to obtain the 
	appropriate value for something (or at least, more complicated than that 
	permitted by Check functions and the other standard mechanisms). 
	Code constants are your friends here.  You can use code constants 
	almost everywhere you can use standard constants -- there are a few 
	[Setup] entries that they don't make sense for, and of course 
	there's no point at all in using a code constant with 
	ExpandConstant.
	The format is very simple, and explained fairly well in the help file.
	You can use either this form: {code:FunctionName} or
	this one: {code:FunctionName|Parameter}.  Note in
	particular that there can be only one parameter, which is passed as a string
	and does not need quotes around it (in fact, if you put quotes in
	then they will be passed into the function as well).
In the [Code] section, you must declare a corresponding function as follows:
		function
		FunctionName(Default:
		String):
		String;
	 | 
You can of course change the function and parameter name to whatever you wish, but the rest must stay the same. Note that even if you are calling it using the first form (specifying no parameter), the parameter is still required -- it will simply be assigned an empty string. You're free to either use the parameter or ignore it, as you wish. The function will only be called when Inno needs to obtain the actual value -- so if it already knows that the entry you've used it on should not be installed, then the function will never be called.
	Just like Check functions, order is unimportant here -- you do
	not need to declare or define your function earlier than the line that uses
	the {code:...} constant.  And also just like Check
	functions, it's possible for the function to get called multiple times, even
	if it's only being used on a single entry.  So it's best to keep things
	simple.  In fact, typically these functions do little more than return the
	contents of a global variable that was calculated earlier.
#include
	I've said before that you should try to keep related routines together.
	But even doing that doesn't always help, when you have a lot of routines
	in the same script file.  The answer?  Break them out into different files,
	of course!  This is what the #include directive is for.  And
	it's even supported by native Inno, so you don't need to have ISPP installed
	to use it.
	So what does #include do?  Basically it takes the contents of
	the named file and treats it exactly as if you'd copied and pasted the whole
	thing at the exact position where the #include line was.
	Thereby including one file within the other one (hence the name).
First of all, of course, you need to create a second file to put things into. This can be called whatever you want, and have any extension you want. I usually use .isi (for Inno Setup Include), but you'll probably find it easier to just keep using .iss (if you use any other extension, you have to keep switching to All files or you won't be able to see them in the Open dialog).
	The very first line of the file should be the [Code] section opening.  This
	is fairly important -- don't even put any comments above this line.  Why?
	Because [Code] and regular Inno script have different comment styles, and
	either could be in effect (it depends on where the #include is
	located).  So it's safest to begin with a known state.
	Next, put the global variables you want to define here, and finally the
	routines.  It's important to remember that everything shares a common
	namespace, so you can't define a variable called "NameEdit"
	(for example) in both places.  For this reason, you may want to put some
	sort of prefix (for example, "MyPage_" for MyPage.isi)
	on the names that are supposed to be "private".
	Now you need to put the #include line into your main script.
	Remember that everything in the file gets treated as if it had been pasted
	in where the #include line itself is.  The normal rules of
	Pascal still apply -- you must declare something before you can use it.
	This means that where you put the line is very important.  If possible,
	you need to put it after everything from the main script that it uses,
	but before everything that uses it.  You may need to rearrange your functions
	a little to achieve this, or insert some forward
	references.
The layout that I typically use is as follows (this is by no means the only way to do it, nor possibly even the best way to do it. It's simply the way I do it):
#includesInitializeWizard)I put "private" in quotes there because as I said earlier, these functions are not truly private -- they're actually accessible to anything after the #include line itself. Another possible structure goes like this:
#include .ish files#include .isi files.ish files
		.isi files
		
	This structure is a bit more work.  It focuses on preventing your main script 
	from calling anything in the include files (apart from the designated 
	entrypoint routines) -- which the first didn't.  It won't prevent your include 
	files from calling routines in the main script that they shouldn't (which the 
	first does). Neither one will protect against one include file calling 
	something in another (which shouldn't be allowed, if you're maintaining proper 
	separation) -- however there is a way to test for that.  Once you've gotten 
	everything compiling and working properly, simply reverse the order of all the 
	#include lines (don't swap the .ishes and the 
	.isis, just the lines within each group).  If it still compiles 
	and runs after that, then you can be confident that there's no coupling 
	between your include files.
	For more detailed examples of #include files in action, see my
	ISD article.  And for more information on one of the
	main drawbacks to include files (and a workaround), see 
	Debugging with include files.
Having your script divided across multiple files is both good and bad for readability -- it's good for code isolation (keeping functions self-contained) and for keeping related routines together. It's not so good for browsing, as the Inno IDE can (at the moment) only open a single file at a time. (Though you can, of course, use a different editor as your IDE.) And unfortunately, it's absolutely terrible for debugging -- again, because the IDE can only load a single file. And here your options are more limited, since the Inno IDE is the only thing that can do debugging. So what can we do?
Well, you've basically got three options (though there are other variations on these themes):
MsgBox around the problem area, and then just 
		run without debugging.  Which boxes come up and what they contain can give 
		you quite a bit of information.  Don't forget to remove the calls once
		you're done! :)#include line, and then
		paste all that code in its place.  Now you can do your debugging.  When
		you're done, you can cut the code back to the clipboard, uncomment the
		include, and paste the code back into the original include file.#includes), put the 
		following line:
		
				#expr SaveToFile("debug.iss")
			 | 
debug.iss will get overwritten.  You'll also need to make sure, 
		before each isolated debugging session, that the #expr line is 
		still the very last one in the file.  Some editor programs will insert new 
		sections at the end of the file, thereby putting the #expr in 
		the wrong place.
	Best practise is to not alter the user's system in any way, until they click 
	the final Next button (on the wpReady page).  Sometimes it's 
	tempting to carry out commands in response to the user's selection on custom 
	wizard pages, but you should fight that urge.  The user must always be allowed 
	to go back and change their mind, or even cancel the entire installation, 
	without any consequences.  As a result, the wizard pages are used simply as 
	information-gatherers, with action to be performed later.  When is the best 
	time?  That depends on what you're doing.
  There are two places where it's nice and easy to attach additional actions to
  (in addition to the BeforeInstall
  and AfterInstall parameters).  Naturally enough, these are
  the start of the installation process, and its end.  I'll start with a
  snippet:
procedure DoPreInstall(); begin // ... end; procedure DoPostInstall(); begin // ... end; procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep = ssInstall then begin DoPreInstall(); end else if CurStep = ssPostInstall then begin DoPostInstall(); end; end;  | 
	This is your starting point.  CurStepChanged is of course one of
	the standard Inno event routines, and can only be defined once in your script
	-- so if you've already got one, then you'll need to merge them (most likely
	by moving code from your existing procedure into either DoPreInstall
	or DoPostInstall).  And speaking of those routines:
DoPreInstall will be called after the user clicks Next on
		the wpReady page, but before Inno installs any of the [Files]
		and other standard script items.DoPostInstall will be called after Inno has completed
		installing all of the [Files], [Registry] entries, and so forth, and also
		after all the non-postinstall [Run] entries, but before the
		wpInfoAfter or wpFinished pages.  However it may
		still be before some COM components are registered -- if Inno decides that
		a reboot is required, it won't carry out the regserver until
		after the reboot.
	I won't go into too much detail here, as there are much better places to find
	information.  Your first port of call should be Inno's help file --
	specifically the "Custom Wizard Pages" topic, and the "Support Functions
	Reference" and "Support Classes Reference".  From there, you should also look
	at two of the example scripts supplied with Inno, namely CodeDlg.iss
	and CodeClasses.iss.
Once you're familiar with the basic idea, you might also find it useful to read my ISD specification (which is by no means an official Inno spec, it just seems to me like a good way to do things).
But just as a quick overview, if you're coming to Inno 5 from having previously used custom pages in Inno 4, you'll find it considerably different.... but better :-) Rather than having to implement all the page-tracking yourself, it's all handled internally by Inno in the same manner as its own standard wizard pages, thereby allowing you far greater flexibility with much more readable code.
	The general idea is that you should create all the wizard pages that could
	be used by your setup up front, inside the InitializeWizard
	handler -- even if most of the time the user will never see that page.  This
	allows Inno to keep proper "housekeeping" and ensures that pages don't get
	mysteriously duplicated, which can happen if you try to follow the Inno4
	model of creating the pages as the user reaches them.  Instead, Inno provides
	the ShouldSkipPage handler, which lets you determine under what conditions
	the page should be shown and what conditions it should be skipped.
	There are two different "groups" of custom pages provided by Inno.  The
	first are what I tend to call "template pages", and are created by the
	various CreateInputXXXPage and
	CreateOutputXXXPage functions.  These are
	prestructured for gathering or displaying a specific type of information,
	and provide lots of additional properties and methods to help you with that
	task.  Unless your needs are esoteric, these are what you'll almost always
	use when building custom pages.  You'll find examples of these in the
	CodeDlg.iss example script.
	The second are true custom pages, created by means of
	CreateCustomPage.  This gives you a blank slate on which you
	can put whatever controls you want, in whatever layout you want.  Naturally,
	you lose out on the helper functions, but you gain in flexibility.  You'll
	find examples of this in the CodeClasses.iss example script.
And that's all I'll say on the topic for the moment. Review the examples, the help, and the ISD page, and that should get you on your way to developing custom pages of your own!
One common mistake people make when they start implementing their own custom pages is to take action the moment the user clicks on the Next button -- perhaps by writing some information to the Registry, perhaps even by beginning an automatic uninstall of the previous version of the application.
Don't do that.
	Go back and read again what I said in Hooking into the start
	or end of the installation.  A user of your setup program should be able
	to click their way all the way up to the wpReady page, then
	suddenly change their mind and click Cancel -- and their system should not
	have been changed in any way.  Only once they pass wpReady and
	agree to start the installation itself should it start to modify things.
	The upshot of all this is that while you might be asking all these questions
	of the user in your shiny new custom pages, you should simply save the
	results until later on.  Once the installation has actually begun, then it's
	time to pull those answers out and act on them.  Perhaps in
	CurStepChanged(ssInstall) or CurStepChanged(ssPostInstall),
	or even a BeginInstall/EndInstall
	handler somewhere.  But not before then.
This article is still a work in progress. I will be gradually extending it, as I get time, as new ideas come to me, and as I receive feedback on what areas you think it would be useful to cover. To submit some feedback, just use the link in the menu to the left (though you'll need to scroll back to the top of the page to see it). Don't be shy -- I welcome feedback of any sort, whether it is a simple comment, a suggestion, or a death threat wrapped around a brick. It's all good. Well, mostly all good :-) You'll also find me hanging around in the Inno support forums. I even answer questions occasionally.
          
  | 
        
          webmaster@mirality.co.nz Website copyright © 2002-08 by Mirality Systems. Mirality Systems, MSI, Ultra Enterprises, UE, and their respective logos are trademarks of Gavin Lambert.  |