WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

Tutorials
plus

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Pointers

 Keywords: Pinter Pointers Scope 

Pointers Explained

Introduction

Pointers can be confusing and generally only used by experienced programmers. This tutorial is intended to give those programmers a jump start into using pointers in their WinBatch scripts.

Pointers can sometimes be used to create more compact and efficient code.


Variable Scope

Each executing WinBatch program has its own variable space, or "scope". Many times before you use a variable it must be defined. You can not define a variable at any spot in the program and expect to use it in any spot, there is a specific relationship between where a variable is defined and where it can be used. This is known as the Scope of the variable.

Scope
The context within which a variable is defined.

Program-Level ('global') Scope
The top level context of the program. Variables that are defined at the top level of the program (outside of any user-defined functions) have program-level scope. For example, all variables in the main code, user defined subroutines, subroutines and called scripts have program-level scope.

Function-Level ('local') Scope
The context of a User Defined Function. Variables that are defined within the context of a user defined function have function-level scope. For example, the all variables that are only accessible within that same user defined function have function-level scope.

Variables that are declared at the top level of the program (outside of any user-defined functions) have program-level or "global" scope. They are visible from all top-level (program-level) code, from any user-defined subroutines called from top-level code, and from any external scripts called from top-level code using the "Call" function. These all share the same variable space, so that if you declare a variable "x" and then call a user-defined subroutine, the user-defined subroutine can change the value of "x" and it will keep its new value even after the user-defined subroutine has completed.

Each user-defined function executing within a WinBatch program has its own variable space, with function-level or "local" scope. A user-defined function does not see variables declared in the program (or user-defined function) that called it, so a variable "x" declared in a user-defined function is a different variable than the variable "x" declared in the program above it, and they can each have different values. Local variables within a user-defined function are visible from within the user-defined function itself, from any user-defined subroutines called from that user-defined function, and from any external scripts called from that user-defined function using the "Call" function.

A user-defined subroutine shares the same variable space as the code that called it. An external script called using the "Call" function also shares the same variable space as the code that called it.

A user-defined function has its own variable space, and does not share the same variable space as the code that called it.

In conclusion, scope basically refers to the visibility of variables. In other words, which parts of your program can see or use it. Normally, every variable has program-level scope. Once defined, every part of your program can access a variable (except for UDFs).

However, sometimes it is very useful to be able to limit a variable's scope to a single function. In other words, the variable will have function-level scope. This way changes inside the function can't affect the main program in unexpected ways.

For example:

#DefineFunction FirstUDF()
    local_var = "FUNCTION-LEVEL SCOPE"
    Message("FirstUDF", StrCat("local_var =" , local_var))
    SecondUDF()
    Return
#EndFunction

#DefineFunction SecondUDF()
    Message("SecondUDF", StrCat("local_var =" , local_var))
    Return
#EndFunction

main_var = "PROGRAM-LEVEL SCOPE"
Message("Main Code", StrCat("main_var = " , main_var))
FirstUDF()
Exit

This program displays:

Main Code
main_var = "PROGRAM-LEVEL"

FirstUDF
local_var = "FUNCTION-LEVEL"
Then errors with:
2058: Function syntax error 
in Routine SecondUDF
On line: Message("SecondUDF", StrCat("local_var =" , local_var))
The 'Additional Error Info' button displays:
Uninitialized variable or undefined function
local_var
The output from this example shows that SecondUDF() could not access the local_var variable that was defined inside FirstUDF(). WIL displays an error message that warns about the uninitialized value.


Pointer Fundamentals

Link
A link is some coded information (address) with which WinBatch can locate a specific variable.

Pointer
A pointer is a special variable which contains a link to a standard WIL variable.

In order to work with pointers some new operators have been defined:

You may notice that the '&' operator and the '*' operator are involved in using pointers. Initially this may be confusing, as the '&' looks like a bitwise 'AND' operator and the '*' looks like the multiply operator. But this is the way C does it and we are just copying C's methodology. Winbatch (and C) can tell by the context whether the '&' and the '*' are math or pointer related operators.

Here is some sample code that demonstrates the use of pointers:

a = 15 ; Define a standard variable.
ptr_a = &a ; Create a link that is then stored in the 'ptr_a' pointer.
Message("Value of ptr_a", ptr_a)   ; Show the link stored in 'ptr_a'.
Message("Dereference ptr_a", *ptr_a) ; Dereference 'ptr_a' to get contents of variable 'a'.
b = 30 ; Define a standard variable.
ptr_b = &b ; Create a link that is then stored in the 'ptr_b' pointer.
*ptr_b = 99 ; Redefine the variable 'b' via a dereference of pointer 'ptr_b'.
Message("Updated Value of b", b) ; Show the current value of variable 'b'.


Special Variables & Pointers Explained

Global Variable
Any variable defined in the top level of the script (i.e. not defined in a UDF), and marked as a global variable with the ptrGlobalDefine function.

Persistent Variable
A variable defined within a UDF using the ptrPersistent function. Persistent variables have the special property of being permanent. The variables not destroyed or re-initialized when the UDF returns and they are available, unchanged for subsequent calls to the UDF.

Global Pointer
A pointer that references a Global variable. Global pointers can reference Global variables from any scope in a script, including UDF's, UDS's and subroutines.

Persistent Pointer
A pointer that references a Persistent variable. Persistent pointers can only reference variables in the same function-level scope (UDF) where it was previously created with the ptrPersistent function.


WIL Pointer Functions

WIL provides some functions that allow you to define and access global pointers: These are all defined in the Windows Interface Language help file, but are provided here for you to examine.


PtrGlobalDefine

Global pointers are declared by using the PtrGlobalDefine. PtrGlobalDefine marks a variable as being globally accessible.
Syntax:
PtrGlobalDefine(variable-name)
Parameters:
(s) variable-name	specifies a variable name.
Returns:
(s) pointer	a pointer string.
This function can only be called from the main script, not from a User Defined Function. If "variable-name" does not already exist, it will be created.

PtrGlobal is used to retrieve a "pointer" to a global variable already defined by PtrGlobalDefine. PtrGlobal can be used to access a variable defined in the main section of code from within a User Defined Function. It can also be used to update values of variables in the main code.


PtrGlobal

Retrieves a "pointer" to a global variable.
Syntax:
PtrGlobal (variable-name)
Parameters:
(s) variable-name	specifies a variable name.
Returns:
(s) pointer	a pointer string.
"variable-name" must have been previously been marked as global using PtrGlobalDefine.
Example:
#DefineFunction CheckGlobalValue()
   ; Retrieve "pointers" to global variables.
   Ptr_Global_A = PtrGlobal( Global_A )
   Ptr_Global_B = PtrGlobal( Global_B )

   ; If the dereferenced value of Ptr_Global_A is not equal to 'potatoes' then error
   If  *Ptr_Global_A != "Potatoes"
      Message("Error","Global_A is not the expected value")
   EndIf

   ; Update the value of the variable Global_B referenced by Ptr_Global_B
   *Ptr_Global_B = 1234
#EndFunction

;Declare Global Variables
PtrGlobalDefine( Global_A )
PtrGlobalDefine( Global_B )

Global_A = "Potatoes"
retvalue = CheckGlobalValue()

; Check if Global_B was updated by the UDF
If Global_B != 1234
   Message("Error","Global_B is not the expected value")
EndIf


Finally, PtrPersistent marks a variable as Persistent. If called from a UDF, the variable will retain its value after the UDF returns. If variable does not already exist, it will be created. If it already exists, its value will not be changed.

A "Persistent" variable is defined and used in a single UDF. But unlike other UDF variables, it "stays around". Kind of like a private global variable that no other UDF's can see. For example, if a UDF wants to save away some information that it needs, it can use a Persistent variable. The next time the UDF is called, its persistent variable is still there, with what ever value was left over from the previous call to it.

PtrPersistent

Creates a "pointer" to a Persistent variable.
Syntax:
PtrPersistent(variable-name, value)
Parameters:
(s) variable-name	specifies a variable name which CANNOT be longer than 25 characters.
(s/i) value	specifies a value which will be assigned to "variable-name" if the variable does not already exist.
Returns:
(s) pointer	a pointer string.

Example:
#DefineFunction IncMyCounter(flag)
       ; flag = 0 will report on current value of counter
       ; flag = 1 will increment counter first

       ;Creates a "pointer" to a persistent variable.
       Ptr_MyCounter = PtrPersistent(MyCounter,0)

       If flag==1 ; report on number of calls
          ;increment persistent variable
          *Ptr_MyCounter = *Ptr_MyCounter + 1
       EndIf
       ;Return the value of the persistent variable
       Return *Ptr_Mycounter
#EndFunction

;Create a random number
r=Random(100)
;Loop a random number of times
For xx = 1 To r
   ;Call IncMyCounter UDF to increment counter
   IncMyCounter(1)
Next

;Call IncMyCounter UDF to report on current value of counter
cnt=IncMyCounter(0)
Message("Counter Value is",cnt)


Using Global Pointers

Global variables are defined and specially marked as global variables using the ptrGlobalDefine function, usually at the very beginning of the script. The ptrGlobalDefine function also alerts you that the variable can be accessed and possibly changed anywhere without limitation in the script. Global variables are especially handy when there is certain constant information that needs to be seen throughout the script. For example, if you have a script that used by several different companies. The company name could be a global variable and can be used throughout the script without having to redefine it or pass it as a parameter to a UDF.
#DefineFunction ShowCName()
   ; Note that this function is not passed the company name via parameters
   ; It uses the ptrGlobal function to reach out and acquire a pointer
   ; to the global company name variable, which it then displays via
   ; a dereference of the previously acquired pointer.
   ptr_CName = PtrGlobal(companyname)
   Message('Company Name Is', *ptr_CName)
   Return
#EndFunction

companyname = 'Wilson WindowWare Inc'  ; Defined and initialize a company name variable.
PtrGlobalDefine(companyname) ; Mark variable as being Global.
ShowCName() ; Note: Companyname information is not passed to the UDF.
            ; Without use of global pointers the UDF could not otherwise access
            ; the company name information.
Exit

Scope-less (Global) Variable

Lets say you want to create a variable that you can access from anywhere in your script. A scope-less variable is any variable defined at the program-level scope of the script (i.e. Not defined in a UDF), and marked as a global variable with the ptrGlobalDefine function. Once the variable is defined using ptrGlobalDefine. You can access the contents of that variable from any scope level in the script.

For Example:

#DefineFunction CheckGlobalValue( )
   ; Pre-define variable ret for success (@true)
   ret = @TRUE

   ; Retrieve a "pointer" to a global variable.
   Ptr_Global_A = PtrGlobal( Global_A )

   ;Check if the dereferenced value of the Global_A is 999
   If  *Ptr_Global_A != 999 Then ret = @FALSE

   ;Returns the result to program level scope.
   Return ret
#EndFunction

;Create a global variable
PtrGlobalDefine( Global_A )

;Define the global variable Global_A's value
Global_A = 999

;Execute UDF that checks Global_A's value is 999
retvalue = CheckGlobalValue( )

;Check return value from CheckGlobalValue UDF
If retvalue == @TRUE
   Message( "Check Global Value", "Expected Value" )
Else
   Message( "Check Global Value", "Un-Expected Value" )
EndIf

The above example :

  1. Creates a global variable called Global_A, using the PtrGlobalDefine function.
  2. Sets the value of Global_A to 999.
  3. Executes a UDF that checks if Global_A's value is 999.
  4. Then within the CheckGlobalValue UDF
    • Pre-defines the variable ret for success (@true).
    • Retrieves a "pointer" to a global variable, using PtrGlobal.
    • Checks the dereferenced value of the Global_A is 999.
    • Returns the result to program level scope.
  5. Checks return value from CheckGlobalValue UDF
  6. Displays the results in a Message dialog.
Here is an example that updates the global variable in side a UDF:
#DefineFunction SetGlobalValue( )
   Ptr_Global_B = PtrGlobal( Global_B )
   *Ptr_Global_B = 1234
   Return
#EndFunction

PtrGlobalDefine( Global_B )
SetGlobalValue( )
If Global_B != 1234
   Message( "Error", "Global_B is not the expected value" )
Else
   Message( "Error", "Global_B is the expected value" )
EndIf
Exit

The above example :

  1. Creates a global variable called Global_B, using the PtrGlobalDefine function.
  2. Executes a UDF that sets Global_B's value to 1234.
  3. Then within the SetGlobalValue UDF
    • Retrieves a "pointer" to a global variable, using PtrGlobal.
    • Sets the dereferenced value of the Global_B is 1234.
    • Returns to program level scope.
  4. Checks the updated value of Global_B.
  5. Displays the results in a Message dialog.


Using Persistent Variables

Another important feature of variable scoping is the 'Persistent' variable. A 'Persistent' variable exists only in the function-level scope (UDF), but it does not lose its value when program execution leaves this scope.

The PtrPersistent function can be used to define a persistent variable. If called from a UDF, the variable will retain its value after the UDF returns.

A "persistent" variable is defined and used in a single UDF. But unlike other UDF variables, it "stays around". Kind of like a private global variable that no other UDF's can see. For example, if a UDF wants to save away some information that it needs, it can use a persistent variable. The next time the UDF is called, its persistent variable is still there, with what ever value was left over from the previous call to it.

Persistent variables are handy when used in a UDF to keep track of what the UDF may have done on a previous call. For example, using a persistent variable, a UDF can easily keep track of how many times it was called.

#DefineFunction GetRandom(task)
   ; Note this function keeps track of how many times it was
   ; called by using a persistent varaible accessed via persistent
   ; pointer, returned by the ptrPersistent function.

   ptr_Count = PtrPersistent(my_counter, 0)
   If task == "GET"
      *ptr_Count = *ptr_Count+1
      Return(Random(1000))
   EndIf
   If task == "REPORT"
      Return *ptr_Count
   EndIf
   Return
#EndFunction

a1 = GetRandom("GET")
a2 = GetRandom("GET")
a3 = GetRandom("GET")
a4 = GetRandom("GET")
a5 = GetRandom("GET")

count = GetRandom("REPORT")
Message("GetRandom Report", StrCat("GetRandom function was used ",count," times"))
Exit


Sample Code

In conclusion, here is a real world example of using Global Pointers. It is a progress meter box that uses global pointers to help keep track of the progress
#DefineFunction UpdateProgress(flag)
   ; Flag:
   ; @False - Initializes the Progress Meter Dialog Box
   ; @True - Updates the Progress Meter Dialog Box
   ; 2 - Kill Progress Meter Dialog Box

   ; Load Shell Operations Extender
   AddExtender("wwsop34i.DLL")

   ; Retrieve Pointers to Global Variables.
   ptr_g_allrecs = PtrGlobal(allrecs)
   ptr_g_donerecs = PtrGlobal(donerecs)

   ; Define Maximum Range
   maxrange = 100

   ; Check Parameter Passed
   Switch flag
      Case @FALSE ; Initialize Progress Meter Dialog Box
         aStatusbar(flag, "Monitoring Progress...", "", maxrange, 0)
         Break
      Case @TRUE ; Update the Progress Meter Dialog Box
         percentdone=(100.0*(*ptr_g_donerecs))/(*ptr_g_allrecs)
         percentdone=Int(percentdone)
         aStatusbar(flag, "Monitoring Progress.", StrCat("Processing ",*ptr_g_allrecs, " Records..."), maxrange, percentdone)
         Break
      Case 2 ; Kill Progress Meter Dialog Box
         aStatusbar(flag, "", "", 0, 0)
         Break
   EndSwitch

   Return
#EndFunction

;############################################################
;#                      MAIN
;############################################################

;Mark Variable as Being Globally Accessible.
PtrGlobalDefine(allrecs)
PtrGlobalDefine(donerecs)

;Initialize the Progress Meter Dialog Box
UpdateProgress(@FALSE)

;Define Total Number of Records to "Process"
allrecs=999
donerecs=0

;"Process" code
For xx = 1 To allrecs
   donerecs=donerecs+1
   UpdateProgress(@TRUE)
Next

UpdateProgress(2)
Message("Notice!","'Process' Complete!")
Exit


Article ID:   W17355
File Created: 2008:04:10:15:08:20
Last Updated: 2008:04:10:15:08:20