Difference between revisions of "Variables - using Global, Local, Static and ByRef"

From AutoIt Wiki
Jump to navigation Jump to search
Line 8: Line 8:
 
- A '''Global''' variable is visible throughout your script - any part of the script can both read its value and, importantly, change it.
 
- A '''Global''' variable is visible throughout your script - any part of the script can both read its value and, importantly, change it.
  
- A '''Local''' variable exists only within the function in which it is declared and is destroyed when the function terminates. It is invisible to any other function unless it is passed as a parameter.
+
- A '''Local''' variable exists only within the function in which it is declared and is destroyed when the function terminates. It is invisible to any other function unless it is passed as a parameter.
  
Additionally declaring a variable as '''Static''' changes its behaviour slightly. A ''Global Static'' variable serves no useful purpose as it is essentially the same as a normal ''Global'' variable. However, a ''Local Static'' variable exists only within the function in which it is declared, but is NOT destroyed when the function ends and can be reused when the function is next entered. You can assign an initial value to this variable when it is created - if it is already in existence then the assignment is ignored. Note that the order of the keywords is not important.
+
Additionally declaring a variable as '''Static''' changes its behaviour slightly. A ''Global Static'' variable serves no useful purpose as it is essentially the same as a normal ''Global'' variable. However, a ''Local Static'' variable exists only within the function in which it is declared, but is NOT destroyed when the function ends and can be reused when the function is next entered. You can assign an initial value to this variable when it is created - if it is already in existence then the assignment is ignored. Note that the order of the keywords is not important.
  
Why do we need to scope variables? Well, it is often useful to have a single variable used throughout the script - perhaps the name of an Ini file which if declared as ''Global'' can then be read by any function that requires it. And ''Local'' variables allow us to save resources by only using variables as long as we need - for example if we create a large array which is only needed within a single function.
+
Why do we need to scope variables? Well, it is often useful to have a single variable used throughout the script - perhaps the name of an Ini file which if declared as ''Global'' can then be read by any function that requires it. And ''Local'' variables allow us to save resources by only using variables as long as we need - for example if we create a large array which is only needed within a single function.
  
We scope variables by using the ''Global, Local'' and ''Static'' keywords when the variables are first declared - look at the examples below. By default, AutoIt scopes any variables declared in the main body of a script (that is not between a ''Func'' and ''EndFunc'' pair) as ''Global'' and any variables declared within function declarations as ''Local''. But it is a good idea to explicitly declare your variables to make sure that you get what you want. For example, you can declare a variable as ''Global'' within a function - although you may get a gentle reminder when you run your script within SciTE that this is not recommended. But there is no point declaring any variables in the main body of a script as ''Local'' - they will be ''Global'' regardless. If you use the ''AutoItSetOption("MustDeclareVars", 1)'' directive you ''must'' declare the scope of your variables or you will get error messages.
+
We scope variables by using the ''Global, Local'' and ''Static'' keywords when the variables are first declared - look at the examples below. By default, AutoIt scopes any variables declared in the main body of a script (that is not between a ''Func'' and ''EndFunc'' pair) as ''Global'' and any variables declared within function declarations as ''Local''. But it is a good idea to explicitly declare your variables to make sure that you get what you want. For example, you can declare a variable as ''Global'' within a function - although you may get a gentle reminder when you run your script within SciTE that this is not recommended. But there is no point declaring any variables in the main body of a script as ''Local'' - they will be ''Global'' regardless. If you use the ''AutoItSetOption("MustDeclareVars", 1)'' directive you ''must'' declare the scope of your variables or you will get error messages.
  
In the Help file pages for ''Global, Local'' and ''Static'' you will also see mention of ''Dim'' and ''ReDim''. Using ''Dim'' will declare a variable and satisfy the ''AutoItSetOption'' just mentioned, but will let AutoIt scope the variable using the default settings as described above. It is much better practice to use ''Global'' or ''Local'' to set the scope you require explicitly. Despite its similar name, ''ReDim'' is only used to resize arrays without destroying the content and is outside the scope of this tutorial.
+
In the Help file pages for ''Global, Local'' and ''Static'' you will also see mention of ''Dim'' and ''ReDim''. Using ''Dim'' will declare a variable and satisfy the ''AutoItSetOption'' just mentioned, but will let AutoIt scope the variable using the default settings as described above. It is much better practice to use ''Global'' or ''Local'' to set the scope you require explicitly. Despite its similar name, ''ReDim'' is only used to resize arrays without destroying the content and is outside the scope of this tutorial.
  
 
Let us now look at a simple example when we read and change ''Global'' variables in a function:
 
Let us now look at a simple example when we read and change ''Global'' variables in a function:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
#include <Constants.au3> ; only required for MsgBox constants
+
#include <MsgBoxConstants.au3> ; only required for MsgBox constants
  
Global $Var_1 = "Variable 1"
+
Global $Var_1 = "Variable 1"
Global $Var_2 = "Variable 2"
+
Global $Var_2 = "Variable 2"
+
 
; Read the variables
+
; Read the variables
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
+
 
; Read the variables in a function
+
; Read the variables in a function
_Function()
+
_Function()
+
 
; And now read the variables again - see that $iVar_2 has changed
+
; And now read the variables again - see that $iVar_2 has changed
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
+
MsgBox($MB_SYSTEMMODAL, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
+
 
Func _Function()
+
Func _Function()
; Read the variables
+
; Read the variables
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
; Now let us change one of the variables WITHIN the function
+
; Now let us change one of the variables WITHIN the function
$Var_2 = "Changed Variable 2"
+
$Var_2 = "Changed Variable 2"
EndFunc
+
EndFunc   ;==>_Function
 
</syntaxhighlight>
 
</syntaxhighlight>
 
Here is a simple example showing how ''Local'' variables are only visible ''inside'' their own function:
 
Here is a simple example showing how ''Local'' variables are only visible ''inside'' their own function:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
#include <Constants.au3> ; only required for MsgBox constants
+
#include <MsgBoxConstants.au3> ; only required for MsgBox constants
 +
 
 +
Global $Var_1 = "Variable 1"
 +
 
 +
; Read the variable
 +
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1)
 +
 
 +
; Now run a function
 +
_Function()
 +
 
 +
; And now try to read Variable 3 outside the function
 +
MsgBox($MB_SYSTEMMODAL, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)
 +
 
 +
Func _Function()
 +
; Declare a Local variable
 +
Local $Var_3 = "Variable 3"
 +
; Read the variables
 +
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)
 +
EndFunc  ;==>_Function
  
Global $Var_1 = "Variable 1"
 
 
; Read the variable
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1)
 
 
; Now run a function
 
_Function()
 
 
; And now try to read Variable 3 OUTSIDE the function
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)
 
 
Func _Function()
 
; Declare a LOCAL variable
 
Local $Var_3 = "Variable 3"
 
; Read the variables
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)
 
EndFunc
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
You should have got errors telling you that ''$Var3'' was "''possibly used before declaration''" and an "''undeclared global variable''".  This is because ''$Var3'' exists only ''within'' the function - when the function ends it is destroyed.
 
You should have got errors telling you that ''$Var3'' was "''possibly used before declaration''" and an "''undeclared global variable''".  This is because ''$Var3'' exists only ''within'' the function - when the function ends it is destroyed.
Line 67: Line 68:
 
This means that we can use the same variable name in different functions without causing a problem - very useful for simple variables which are only used temporarily:
 
This means that we can use the same variable name in different functions without causing a problem - very useful for simple variables which are only used temporarily:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
  #include <Constants.au3> ; only required for MsgBox constants
+
  #include <MsgConstants.au3>; only required for MsgBox constants
  
Function_1()
+
Function_1()
+
 
Func Function_1()
+
Func Function_1()
+
; Declare the variable in this function
; Declare the variable in this function
+
Local $sString = "Variable in Function One"
Local $sString = "Variable in Function One"
+
; Read the value
; Read the value
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
+
 
+
; Now run Function Two
; Now run Function Two
+
Function_2()
Function_2()
+
 
+
; Now read the variable again - we still get the variable from this function
; Now read the variable again - we still get the variable from this function
+
; The variable in Function 2 has been destroyed now the function has terminated
; The variable in Function 2 has been destroyed now the function has terminated
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
+
EndFunc   ;==>Function_1
+
 
EndFunc
+
Func Function_2()
+
; Declare a variable with the SAME name
Func Function_2()
+
Local $sString = "Variable in Function Two"
+
; Read this variable - see how we get the value in this function, not the value in Function One
; Declare a variable with the SAME name
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In Function Two" & @CRLF & @CRLF & "Variable = " & $sString)
Local $sString = "Variable in Function Two"
+
EndFunc   ;==>Function_2
; Read this variable - see how we get the value in this function, not the value in Function One
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In Function Two" & @CRLF & @CRLF & "Variable = " & $sString)
 
 
EndFunc
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
AutoIt even allows you to use the same name for ''Global'' and ''Local'' variables, although this is ''not'' recommended as it could easily lead to confusion.  If there is a naming conflict of this kind, AutoIt uses the ''Local'' value as you can see here:
 
AutoIt even allows you to use the same name for ''Global'' and ''Local'' variables, although this is ''not'' recommended as it could easily lead to confusion.  If there is a naming conflict of this kind, AutoIt uses the ''Local'' value as you can see here:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
#include <Constants.au3> ; only required for MsgBox constants
+
#include <MsgBoxConstants.au3>; only required for MsgBox constants
  
; Declare the variable as Global
+
; Declare the variable as Global
Global $sString = "Global Variable in the Main Script"
+
Global $sString = "Global Variable in the Main Script"
+
 
; Read it
+
; Read it
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)
+
 
; Run the functions which declare Local variables with the same name
+
; Run the functions which declare Local variables with the same name
Function_1()
+
Function_1()
+
 
; And now see that the Global variable still exists unchanged
+
; And now see that the Global variable still exists unchanged
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)
+
 
Func Function_1()
+
Func Function_1()
+
; Declare a variable with the SAME name as Local to this function
; Declare a variable with the SAME name as Local to this function
+
Local $sString = "Variable in Function One"
Local $sString = "Variable in Function One"
+
; Read the value - we get the value of the Local variable in this function, not the Global one
; Read the value - we get the value of the Local variable in this function, not the Global one
+
MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
+
EndFunc   ;==>Function_1
 
EndFunc
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
A good use for ''Static'' variables is when you need to maintain a record of a value within a function even though the function itself ends.  An example might be that you want to do something the first time a function is called, but not subsequently:
 
A good use for ''Static'' variables is when you need to maintain a record of a value within a function even though the function itself ends.  An example might be that you want to do something the first time a function is called, but not subsequently:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
Foo()
+
Foo()
Foo()
+
Foo()
Foo()
+
Foo()
+
 
Func Foo()
+
Func Foo()
; Set the flag only on the first entry into the function
+
; Set the flag only on the first entry into the function
; This assignment will be ignored on subsequent entries
+
; This assignment will be ignored on subsequent entries
Local Static $fFoo_First_Pass = True
+
Local Static $bFoo_First_Pass = True
+
 
; Check the flag
+
; Check the flag
If $fFoo_First_Pass Then
+
If $bFoo_First_Pass Then
; Immediately clear the flag to prevent running on subsequent passes
+
; Immediately clear the flag to prevent running on subsequent passes
$fFoo_First_Pass = False
+
$bFoo_First_Pass = False
; Run on first pass because flag is set
+
; Run on first pass because flag is set
ConsoleWrite("First pass" & @CRLF)
+
ConsoleWrite("First pass" & @CRLF)
Else
+
Else
; Flag remains cleared for subsequent passes
+
; Flag remains cleared for subsequent passes
ConsoleWrite("Not first pass" & @CRLF)
+
ConsoleWrite("Not first pass" & @CRLF)
EndIf
+
EndIf
EndFunc  ;==>Foo
+
EndFunc  ;==>Foo
 
</syntaxhighlight>
 
</syntaxhighlight>
 
As the ''Local'' part of their declaration implies, you can use the same name for ''Static'' variables in different functions - even though they are not destroyed when the functions end:
 
As the ''Local'' part of their declaration implies, you can use the same name for ''Static'' variables in different functions - even though they are not destroyed when the functions end:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
 
  For $i = 1 To 5
 
  For $i = 1 To 5
    _Function_1()
+
_Function_1()
    _Function_2()
+
_Function_2()
Next
+
Next
+
 
Func _Function_1()
+
Func _Function_1()
    ; Declare the variable as Static - remember this only happens once
+
; Declare the variable as Static - remember this only happens once
    Local Static $vVar = 0
+
Local Static $vVar = 0
    ; Increase the value of the variable each time we enter the function
+
; Increase the value of the variable each time we enter the function
    $vVar += 10
+
$vVar += 10
    ; And show that it has retained its value as the function exits
+
; And show that it has retained its value as the function exits
    ConsoleWrite("In Function_1: " & $vVar & @CRLF)
+
ConsoleWrite("In Function_1: " & $vVar & @CRLF)
EndFunc
+
EndFunc   ;==>_Function_1
+
 
Func _Function_2()
+
Func _Function_2()
    ; Use the same variable name
+
; Use the same variable name
    Local Static $vVar = 0
+
Local Static $vVar = 0
    $vVar += 100
+
$vVar += 100
    ConsoleWrite("In Function_2: " & $vVar & @CRLF)
+
ConsoleWrite("In Function_2: " & $vVar & @CRLF)
EndFunc
+
EndFunc   ;==>_Function_2
 
</syntaxhighlight>  
 
</syntaxhighlight>  
  
Line 182: Line 177:
 
Here is a small example to show how parameters are passed when calling a function:
 
Here is a small example to show how parameters are passed when calling a function:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
  #include <Constants.au3> ; only required for MsgBox constants
+
  #include <MsgBoxConstants.au3>; only required for MsgBox constants
 +
 
 +
Func_A()
 +
 
 +
Func Func_A()
 +
; Declare variables
 +
Local $iVar_A1 = 10
 +
Local $iVar_A2 = 20
 +
; And read them
 +
MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
 +
 
 +
; Now pass these variables to another function - we use the names we have already declared in this function
 +
Func_B($iVar_A1, $iVar_A2)
 +
 
 +
; We changed the parameters in Func_B - but nothing happened to the variables in this function
 +
MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
 +
EndFunc  ;==>Func_A
  
Func_A()
+
Func Func_B($iVar_B1, $iVar_B2) ; There is no need to declare the variables as parameters are automatically Local in scope
+
; Now read these variables - note that they have the same value as the variables in Func_A
Func Func_A()
+
MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
+
 
; Declare variables
+
; Let us change them
Local $iVar_A1 = 10
+
$iVar_B1 = 100
Local $iVar_A2 = 20
+
$iVar_B2 = 200
; And read them
+
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
+
; And confirm that they have changed
+
MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
; Now pass these variables to another function - we use the names we have already declared in this function
+
 
Func_B($iVar_A1, $iVar_A2)
+
; Now return to the other function
+
EndFunc   ;==>Func_B
; We changed the parameters in Func_B - but nothing happened to the variables in this function
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
 
 
EndFunc
 
 
Func Func_B($iVar_B1, $iVar_B2) ; There is no need to declare the variables as parameters are automatically Local in scope
 
 
; Now read these variables - note that they have the same value as the variables in Func_A
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
 
 
; Let us change them
 
$iVar_B1 = 100
 
$iVar_B2 = 200
 
 
; And confirm that they have changed
 
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
 
 
; Now return to the other function
 
 
EndFunc
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
As you can see, the ''Local'' variables within ''Func_A'' are read perfectly by ''Func_B'' when passed as parameters, even though they were declared as ''Local'' in ''Func_A''.  However, although ''Func_B'' changes the value of the variables, the values of the original variables within ''Func_A'' are not changed.
 
As you can see, the ''Local'' variables within ''Func_A'' are read perfectly by ''Func_B'' when passed as parameters, even though they were declared as ''Local'' in ''Func_A''.  However, although ''Func_B'' changes the value of the variables, the values of the original variables within ''Func_A'' are not changed.
Line 222: Line 213:
 
If we want to change the value of the variables in ''Func_A'' from within ''Func_B'', we need to use the ''ByRef'' keyword as explained above and illustrated here:
 
If we want to change the value of the variables in ''Func_A'' from within ''Func_B'', we need to use the ''ByRef'' keyword as explained above and illustrated here:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
  #include <Constants.au3> ; only required for MsgBox constants
+
  #include <MsgBoxConstants.au3>; only required for MsgBox constants
  
Func_A()
+
Func_A()
+
 
Func Func_A()
+
Func Func_A()
+
; Declare variables
; Declare variables
+
Local $iVar_A1 = 10
Local $iVar_A1 = 10
+
Local $iVar_A2 = 20
Local $iVar_A2 = 20
+
 
+
; And read them
; And read them
+
MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
+
 
+
; Now pass these variables to another function - but this time we will pass $iVar_A2 By Reference
; Now pass these variables to another function - but this time we will pass $iVar_A2 By Reference
+
Func_B($iVar_A1, $iVar_A2)
Func_B($iVar_A1, $iVar_A2)
+
 
+
; We changed the parameters in Func_B - this time we see that we have changed $iVar_A2
; We changed the parameters in Func_B - this time we see that we have changed $iVar_A2
+
MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
+
EndFunc   ;==>Func_A
+
 
EndFunc
+
Func Func_B($iVar_B1, ByRef $iVar_B2) ; Note the ByRef keyword in the function declaration for the second parameter
+
; Now read these variables - they have the same value as the variables in Func_A
Func Func_B($iVar_B1, ByRef $iVar_B2) ; Note the ByRef keyword in the function declaration for the second parameter
+
MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
+
 
; Now read these variables - they have the same value as the variables in Func_A
+
; Let us change them
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
+
$iVar_B1 = 100
+
$iVar_B2 = 200
; Let us change them
+
 
$iVar_B1 = 100
+
; And confirm that they have changed
$iVar_B2 = 200
+
MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
+
 
; And confirm that they have changed
+
; Now return to the other function
MsgBox($MB_ICONINFORMATION + $MB_TOPMOST, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)
+
EndFunc   ;==>Func_B
 
; Now return to the other function
 
 
EndFunc
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 266: Line 253:
 
- If you are dealing with a simple variable of considerable size (the text of a file for example) then passing it ''ByRef'' is a sensible action.  But if you want to make sure that the variable cannot be altered inside the function, you can declare it as ''Const ByRef'' in the function definition as shown here:
 
- If you are dealing with a simple variable of considerable size (the text of a file for example) then passing it ''ByRef'' is a sensible action.  But if you want to make sure that the variable cannot be altered inside the function, you can declare it as ''Const ByRef'' in the function definition as shown here:
 
<syntaxhighlight lang="autoit">
 
<syntaxhighlight lang="autoit">
Global $sString = "A very large chunk of data which we do not want to change"
+
Local $sString = "A very large chunk of data which we do not want to change"
+
 
Foo($sString)
+
Foo($sString)
+
 
Func Foo(Const ByRef $sText) ; The data is not copied, but also cannot be changed
+
Func Foo(Const ByRef $sText) ; The data is not copied, but also cannot be changed
; Uncomment this line to see what happens when we try
+
; Uncomment this line to see what happens when we try
;$sText = "New value"
+
;$sText = "New value"
EndFunc
+
EndFunc   ;==>Foo
 
</syntaxhighlight>
 
</syntaxhighlight>
  
''ByRef'' is a very powerful tool - but use it carefully, or you may find that you have changed a variable when you did not want to!
+
''ByRef'' is a very powerful feature - but use it carefully, or you may find that you have changed a variable when you did not want to!

Revision as of 04:11, 25 March 2015

These keywords confuse many coders - this tutorial should help make their use clear.

In most programming languages you can define the scope of variables - this determines the visibility or accessibility of the variable from different parts of the program.

In AutoIt there are two possible scopes: Global and Local.

- A Global variable is visible throughout your script - any part of the script can both read its value and, importantly, change it.

- A Local variable exists only within the function in which it is declared and is destroyed when the function terminates. It is invisible to any other function unless it is passed as a parameter.

Additionally declaring a variable as Static changes its behaviour slightly. A Global Static variable serves no useful purpose as it is essentially the same as a normal Global variable. However, a Local Static variable exists only within the function in which it is declared, but is NOT destroyed when the function ends and can be reused when the function is next entered. You can assign an initial value to this variable when it is created - if it is already in existence then the assignment is ignored. Note that the order of the keywords is not important.

Why do we need to scope variables? Well, it is often useful to have a single variable used throughout the script - perhaps the name of an Ini file which if declared as Global can then be read by any function that requires it. And Local variables allow us to save resources by only using variables as long as we need - for example if we create a large array which is only needed within a single function.

We scope variables by using the Global, Local and Static keywords when the variables are first declared - look at the examples below. By default, AutoIt scopes any variables declared in the main body of a script (that is not between a Func and EndFunc pair) as Global and any variables declared within function declarations as Local. But it is a good idea to explicitly declare your variables to make sure that you get what you want. For example, you can declare a variable as Global within a function - although you may get a gentle reminder when you run your script within SciTE that this is not recommended. But there is no point declaring any variables in the main body of a script as Local - they will be Global regardless. If you use the AutoItSetOption("MustDeclareVars", 1) directive you must declare the scope of your variables or you will get error messages.

In the Help file pages for Global, Local and Static you will also see mention of Dim and ReDim. Using Dim will declare a variable and satisfy the AutoItSetOption just mentioned, but will let AutoIt scope the variable using the default settings as described above. It is much better practice to use Global or Local to set the scope you require explicitly. Despite its similar name, ReDim is only used to resize arrays without destroying the content and is outside the scope of this tutorial.

Let us now look at a simple example when we read and change Global variables in a function:

#include <MsgBoxConstants.au3> ; only required for MsgBox constants

Global $Var_1 = "Variable 1"
Global $Var_2 = "Variable 2"

; Read the variables
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)

; Read the variables in a function
_Function()

; And now read the variables again - see that $iVar_2 has changed
MsgBox($MB_SYSTEMMODAL, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)

Func _Function()
	; Read the variables
	MsgBox($MB_SYSTEMMODAL, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 2 = " & $Var_2)
	; Now let us change one of the variables WITHIN the function
	$Var_2 = "Changed Variable 2"
EndFunc   ;==>_Function

Here is a simple example showing how Local variables are only visible inside their own function:

#include <MsgBoxConstants.au3> ; only required for MsgBox constants

Global $Var_1 = "Variable 1"

; Read the variable
MsgBox($MB_SYSTEMMODAL, "Reading", "In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1)

; Now run a function
_Function()

; And now try to read Variable 3 outside the function
MsgBox($MB_SYSTEMMODAL, "Reading", "Back In The Main Script" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)

Func _Function()
	; Declare a Local variable
	Local $Var_3 = "Variable 3"
	; Read the variables
	MsgBox($MB_SYSTEMMODAL, "Reading", "In The Function" & @CRLF & @CRLF & "Variable 1 = " & $Var_1 & @CRLF & "Variable 3 = " & $Var_3)
EndFunc   ;==>_Function

You should have got errors telling you that $Var3 was "possibly used before declaration" and an "undeclared global variable". This is because $Var3 exists only within the function - when the function ends it is destroyed.

This means that we can use the same variable name in different functions without causing a problem - very useful for simple variables which are only used temporarily:

 #include <MsgConstants.au3>; only required for MsgBox constants

Function_1()

Func Function_1()
	; Declare the variable in this function
	Local $sString = "Variable in Function One"
	; Read the value
	MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)

	; Now run Function Two
	Function_2()

	; Now read the variable again - we still get the variable from this function
	; The variable in Function 2 has been destroyed now the function has terminated
	MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
EndFunc   ;==>Function_1

Func Function_2()
	; Declare a variable with the SAME name
	Local $sString = "Variable in Function Two"
	; Read this variable - see how we get the value in this function, not the value in Function One
	MsgBox($MB_SYSTEMMODAL, "Reading", "In Function Two" & @CRLF & @CRLF & "Variable = " & $sString)
EndFunc   ;==>Function_2

AutoIt even allows you to use the same name for Global and Local variables, although this is not recommended as it could easily lead to confusion. If there is a naming conflict of this kind, AutoIt uses the Local value as you can see here:

#include <MsgBoxConstants.au3>; only required for MsgBox constants

; Declare the variable as Global
Global $sString = "Global Variable in the Main Script"

; Read it
MsgBox($MB_SYSTEMMODAL, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)

; Run the functions which declare Local variables with the same name
Function_1()

; And now see that the Global variable still exists unchanged
MsgBox($MB_SYSTEMMODAL, "Reading", "In the Main Script" & @CRLF & @CRLF & "Variable = " & $sString)

Func Function_1()
	; Declare a variable with the SAME name as Local to this function
	Local $sString = "Variable in Function One"
	; Read the value - we get the value of the Local variable in this function, not the Global one
	MsgBox($MB_SYSTEMMODAL, "Reading", "In Function One" & @CRLF & @CRLF & "Variable = " & $sString)
EndFunc   ;==>Function_1

A good use for Static variables is when you need to maintain a record of a value within a function even though the function itself ends. An example might be that you want to do something the first time a function is called, but not subsequently:

Foo()
Foo()
Foo()

Func Foo()
	; Set the flag only on the first entry into the function
	; This assignment will be ignored on subsequent entries
	Local Static $bFoo_First_Pass = True

	; Check the flag
	If $bFoo_First_Pass Then
		; Immediately clear the flag to prevent running on subsequent passes
		$bFoo_First_Pass = False
		; Run on first pass because flag is set
		ConsoleWrite("First pass" & @CRLF)
	Else
		; Flag remains cleared for subsequent passes
		ConsoleWrite("Not first pass" & @CRLF)
	EndIf
EndFunc   ;==>Foo

As the Local part of their declaration implies, you can use the same name for Static variables in different functions - even though they are not destroyed when the functions end:

 For $i = 1 To 5
	_Function_1()
	_Function_2()
Next

Func _Function_1()
	; Declare the variable as Static - remember this only happens once
	Local Static $vVar = 0
	; Increase the value of the variable each time we enter the function
	$vVar += 10
	; And show that it has retained its value as the function exits
	ConsoleWrite("In Function_1: " & $vVar & @CRLF)
EndFunc   ;==>_Function_1

Func _Function_2()
	; Use the same variable name
	Local Static $vVar = 0
	$vVar += 100
	ConsoleWrite("In Function_2: " & $vVar & @CRLF)
EndFunc   ;==>_Function_2

As a general rule, try to have as few Global variables as you can. Remember that you can share Local variables between functions - and even change their values - by passing them as parameters as we will see in the ByRef section below. And declaring a variable as Static allows you retain its value even though the function in which it is used has ended.

Passing variables as parameters using ByRef

When you call a function in AutoIt, you can pass parameters to the function by including them in the parentheses following the function name like this - Function(Parameter_1, Parameter_2). Passing variables in this way means that you can use variables that have been declared as Local within other functions. Note that the parameters are automatically declared as Local variables within the called function and so do not need to be declared again in the code.

Variables can be passed as parameters to a function in 2 ways:

By Value - This is the default. The parameter is treated as a Local variable within the function to which it is passed and any changes made to it are lost when the function ends. So no changes are made to the Local variable within the original function.

By Reference - This is used when ByRef is added before the variable name in the parameter list of the function declaration. Now any changes to the variable made in the function to which it is passed also affect the Local variable in the original function. If you do not understand why this is such a powerful capability, you need to read the Global/Local section again!

Here is a small example to show how parameters are passed when calling a function:

 #include <MsgBoxConstants.au3>; only required for MsgBox constants

Func_A()

Func Func_A()
	; Declare variables
	Local $iVar_A1 = 10
	Local $iVar_A2 = 20
	; And read them
	MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)

	; Now pass these variables to another function - we use the names we have already declared in this function
	Func_B($iVar_A1, $iVar_A2)

	; We changed the parameters in Func_B - but nothing happened to the variables in this function
	MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
EndFunc   ;==>Func_A

Func Func_B($iVar_B1, $iVar_B2) ; There is no need to declare the variables as parameters are automatically Local in scope
	; Now read these variables - note that they have the same value as the variables in Func_A
	MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)

	; Let us change them
	$iVar_B1 = 100
	$iVar_B2 = 200

	; And confirm that they have changed
	MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)

	; Now return to the other function
EndFunc   ;==>Func_B

As you can see, the Local variables within Func_A are read perfectly by Func_B when passed as parameters, even though they were declared as Local in Func_A. However, although Func_B changes the value of the variables, the values of the original variables within Func_A are not changed.

If we want to change the value of the variables in Func_A from within Func_B, we need to use the ByRef keyword as explained above and illustrated here:

 #include <MsgBoxConstants.au3>; only required for MsgBox constants

Func_A()

Func Func_A()
	; Declare variables
	Local $iVar_A1 = 10
	Local $iVar_A2 = 20

	; And read them
	MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)

	; Now pass these variables to another function - but this time we will pass $iVar_A2 By Reference
	Func_B($iVar_A1, $iVar_A2)

	; We changed the parameters in Func_B - this time we see that we have changed $iVar_A2
	MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func A:" & @CRLF & "Var A1 = " & $iVar_A1 & @CRLF & "Var A2 = " & $iVar_A2)
EndFunc   ;==>Func_A

Func Func_B($iVar_B1, ByRef $iVar_B2) ; Note the ByRef keyword in the function declaration for the second parameter
	; Now read these variables - they have the same value as the variables in Func_A
	MsgBox($MB_SYSTEMMODAL, "Read", "Initial read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)

	; Let us change them
	$iVar_B1 = 100
	$iVar_B2 = 200

	; And confirm that they have changed
	MsgBox($MB_SYSTEMMODAL, "Read", "Second read in Func B:" & @CRLF & "Var B1 = " & $iVar_B1 & @CRLF & "Var B2 = " & $iVar_B2)

	; Now return to the other function
EndFunc   ;==>Func_B

A couple of more advanced points:

- It was mentioned earlier that when you pass variables to functions AutoIt makes local copies of these variables unless you declare that they are to be passed ByRef. In the special case of arrays, which are typically larger than a simple variable, AutoIt will only make a local copy if you change the array inside the function - merely accessing the array will use the original version. So passing the array ByRef is only advantageous if you intend to modify the array within the function - however, it does no harm to force the issue by using the keyword.

- If you are dealing with a simple variable of considerable size (the text of a file for example) then passing it ByRef is a sensible action. But if you want to make sure that the variable cannot be altered inside the function, you can declare it as Const ByRef in the function definition as shown here:

Local $sString = "A very large chunk of data which we do not want to change"

Foo($sString)

Func Foo(Const ByRef $sText) ; The data is not copied, but also cannot be changed
	; Uncomment this line to see what happens when we try
	;$sText = "New value"
EndFunc   ;==>Foo

ByRef is a very powerful feature - but use it carefully, or you may find that you have changed a variable when you did not want to!