Overview
GFA-BASIC provides five different keywords to start a subroutine all with a different meaning and purpose. In short:
| Compatibility | Variable passing | Default type | Return type | |
| Sub | VB and GFA | ByRef | Variant | |
| Procedure Proc |
GFA | ByVal | Double | |
| Function | GFA | ByVal | Double | Double |
| FunctionVar | VB | ByRef | Variant | Variant |
The Procedure (or its shortcut Proc) and Function keywords are typical GFA-BASIC instructions. The Sub and the FunctionVar are new for GFA-BASIC 32 and are added for VB compatibility. The Sub is specifically used for OCX (form and control) events. A Sub takes its arguments by reference (ByRef) by default. (GB32 has a terrible flaw, see below). In addition, for VB compatibility reasons, the default type for an argument is Variant. This is in contrast with Procedure and Function instructions, which are GFA-BASIC (16-bit) compatible and take arguments by value (ByVal) and type Double by default. The FunctionVar is only meant to be used when using VB code.
Two types of procedures
In GFA-BASIC 32, it's useful to distinguish between two types of procedures, general procedures and event procedures. It is advisable to distinguish them by defining general procedures using the Procedure keyword.
General procedures
A general procedure tells the application how to perform a specific task. Once a general procedure is defined, it must be specifically invoked by the application. By contrast, an event procedure remains idle until called upon to respond to events caused by the user or triggered by the system.
Why create general procedures? One reason is that several different event procedures might need the same actions performed. A good programming strategy is to put common statements in a separate procedure (a general procedure) and have your event procedures call it. This eliminates the need to duplicate code and also makes the application easier to maintain.
To distinguish general procedures from event procedures and to prevent the Sub - ByRef error (see below), it is logical to define general procedures using the Procedure or Proc keyword.
Event Procedures
When an OCX in GFA-BASIC 32 recognizes that an event has occurred, it automatically invokes the event procedure using the name corresponding to the event. Because the name establishes an association between the object and the code, event procedures are said to be attached to forms and controls.
- An event procedure for a control combines the control's actual name (specified in the Name property), an underscore (_), and the event name. For instance, if you want a command button named cmdPlay to invoke an event procedure when it is clicked, use the procedure cmdPlay_Click.
- An event procedure for a form combines the word "frm," an underscore, and the event name. If you want a form to invoke an event procedure when it is clicked, use the procedure frm_Click. (Like controls, forms do have unique names, but they are not used in the names of event procedures.)
All event procedures use the same general syntax.
| Syntax for a control event | Syntax for a form event |
| Sub ocx_eventname (arguments ) statements End Sub |
Sub frm_eventname (arguments) statements End Sub |
Although you can write event procedures from scratch, it's easier to use the code procedures provided by GFA-BASIC 32, which automatically include the correct procedure names. You can select a template in the Code Editor window by selecting an event procedure from the Properties box.
It's also a good idea to set the Name property of your controls before you start writing event procedures for them. If you change the name of a control after attaching a procedure to it, you must also change the name of the procedure to match the new name of the control.
Using VB code
GFA-BASIC is vented as being VB compatible. However, this is only partly true. Here we will look at the differences between the VB and GFA-BASIC 32 Sub and Function keywords.
The Sub keyword is explicitly added to the language to be VB compatible. Its first purpose is to start an OCX event procedure. The other main purpose is to make copying, pasting, and using VB code as easy as possible. This works as intended, but unfortunately GFA-BASIC has a terrible flaw in passing around variables ByRef when the Sub argument does not contain a ByRef keyword explicitly. See for more details below. In short, you can use Sub procedure for events and VB code, but any non-event Sub must contain the ByRef keyword for by reference variables explicitly. This means some manual editing from your part.
The VB Function keyword is not the same as the GFA-BASIC 32 Function keyword. You must replace it by the FunctionVar keyword, therefore! Only then you can be sure the function behaves exactly as the VB function. (Note, the keyword could also be having named 'FunctionVB' for that matter). Once you got the VB code to work, you can change back to the GFA-BASIC 32 Function keyword. However, you should be very careful, because you must change the default behavior of the function. Each untyped argument is to changed to 'As Variant', and ByRef must be inserted explicitly. Also, note the difference in the function's return value. VB returns a Variant and GFA-BASIC a Double. If the return type is missing, you must append As Variant at Function header as well.
It is extremely important (I know now) to apply these two rules when using VB code. You will introduce very hard to find errors if you don't use FunctionVar or don't add ByRef to non-event Subs.
The Sub-ByRef flaw
A call to a Sub procedure using by reference arguments might not update the correct variable. To understand this behaviour be sure to understand the differences between ByVal and ByRef arguments. This is explained in the next tip 'Passing values ByRef or ByVal'. Here you have learned that a Sub by default takes arguments as ByRef, making ByRef optional. Unfortunately this is not always true.
When an argument is passed by reference, the procedure is passed the address of the argument variable. When the argument is a global variable the address of the variable is passed as expected. In the following situation the ByRef clause is omitted and the global variable is updated to 100 correctly.
Global a% = 50
MySub(a%)
Print a% ' 100
Sub MySub(b%)
b% = 100
EndSub
When a second Sub is called from within MySub passing b% by reference, b% is not updated as expected. The variable b% is passed by value, rather than by reference!
Sub MySub(b%)
b% = 100 : NextSub(b%) : Print b% // still 100
EndSub
Sub NextSub(c%)
c% = 200 // unexpected: c% is ByVal argument
EndSub
Why this is happening is unclear, but it is easily repaired by using ByRef explicitly in Sub headings.
Sub NextSub(ByRef c%)
Passing values ByRef or ByVal to subroutines?
Procedures are an essential part of almost every GB32 program. When you define a procedure, whether it's a Function or a Sub procedure, you need to decide whether the procedure arguments are passed by reference or by value. What difference does it make? Like VB the GFA-BASIC Sub instruction's default is to pass arguments by reference. You can include the ByRef keyword in an argument list if desired but, because this is the default, it should (!) have no effect:
Sub Foo(ByRef Arg1 As Integer, ByRef Arg2 As String)
Sub Foo(Arg1 As Integer, Arg2 As String)
When an argument is passed by reference, the procedure is passed the address of the argument variable (in other words, a reference to the variable):
Dim Total as Integer
MySub(Total)
In this example, MySub receives a reference to Total. The practical consequence of this is that code in MySub can change Total. Here's an example.
Dim Total As Integer = 100
MySub(Total)
Print Total ' 50
Sub MySub(Total As Integer)
Total = 50
End Sub
When you use ByVal, the procedure is passed a copy of the argument variable and not a reference to the argument variable itself. Code in the procedure cannot change the variable's value. After this code executes, Total is still equal to 100.
Sub MySub(ByVal Total As Integer)
Total = 50
End Sub
For most procedures, the default ByRef argument passing is fine. You can use ByVal when you want to ensure that code in the procedure cannot change the variable that was passed as an argument. You also must use ByVal when declaring Windows API and other DLL functions for use in your programs.
In addition to the Sub procedures, GFA-BASIC offers the Procedure or Proc statement to declare GFA-BASIC 16 compatible routines. The Proc(edure) instruction's default is to pass arguments by value, rather than by reference as Sub does. You can include the ByVal keyword in an argument list if desired but, because this is the default, it has no effect.
Proc MyProc(Total As Integer) ' ByVal is default
Total = 50
End Sub
The same is true for Function subroutines; they take arguments by value as well. This differs from VB where functions take arguments by reference also.