(c) 2000, 2001 John Findlay & Joe Hurst
January 2001 - Release 1.1
Various mistakes were noticed in the previous version of this red paper - this version aims at correcting those mistakes. Some new features, or features that were not known before have also been added. Please use this version of The Red Paper.
Be so kind and do not change anything in this text; please contact us with your contribution and we'll do the changes.
How To Use A Library In A GB32-Source
Developing And Debugging Guide
Abstract Or How To Export What >From A Library
About Procedures And Functions
After years of waiting, GFA released GFA-BASIC 32 (32 bit) at the end of 1999 to replace the old GFA-BASIC for Windows 3.0 (16-bit). GB32 (this is the official abbreviation for GFA-BASIC 32) was promised as a tool for rapid application development, containing OOP-features, libraries, editor-extensions, resource construction set etc. etc.
GB32 has many of the promised features, but improvements need to be made if this product is to compete on the world stage:
Although there are shortfalls in implementation, at least GFA-BASIC 32 is as fast as usual, coming close to matching the speed of the best 'C' compilers. To build large applications one can rely on libraries to some extent but the inclusion of source listings (as part of a project) would help a great deal.
So, why Red Paper, why not! Green, blue - we informally choose red.
The success of GB32 depends on continued support and development from GFA - we will release new red papers as seems appropriate for new topics.
Since version 2.0, GB32 allows the use of 'libraries'.
Note: Because of many problems with GB32 2.0 when building libraries we suggest you upgrade to version 2.1 as soon as possible.
Libraries are compiled GB32-sources with procedures, functions, constants and other variables (this collection is called "elements" in the following) which are linked into the current source where the elements are available for use as if they were directly parts of the current source.
Libraries can also be used to create special functions for other GB32-programmers who need not trouble themselves with the source code as the library will already be compiled as a binary.
Libraries are very important and useful when creating applications with a collection of modules. These modules should be re-useable robustly written procedures and functions; in addition one would also include constants, types and declared API calls etc.
Libraries for GB32 must be compiled before they can be used in a source. The extension for a compiled library is ".LG32" (Library for GFA-BASIC 32), the extension for the source of a library is ".G32" (GFA-BASIC 32). While you can use the double extension that GFA proposed for library source code, ".lib.g32", one may as well use any well defined system of one's own.
For example:
MyFunctions(Src).g32 (Source code of Library)
MyFunctions(Lib).lg32 (Compiled code of Library)
Libraries created with GB32 version 2.0 have many problems but with version 2.1 we expect to see great improvements allowing programmers to produce some good modular code.
Here is the three-step-how-to which always leads to success:
$library "libraryname[.lg32]"
and press enter.
The directive $Library informs the IDE that you are importing a library. "libraryname" is the name of the library you wish to import and it must be enclosed in quotes (" "). You may also include the path of the library if necessary. The extension ".lg32" is optional.
The previous version of this Red Paper (vers 1.0) stated that one can use the $Library directive to import multiple libraries on one line, this is not the case. One $Library statement imports one library.
Like so;
$Library "MyLib1"
$Library "MyLib2"
Now, after the line including the library statement has been entered the IDE immediately tries to load the library:
When compiling/running a source containing a library import, the library is linked into the executable by the compiler making all exported 'elements' available to the current source code. Any functions contained within the library that are not used by the current source will not be linked making the compilation with libraries Economic.
Libraries are internally "inserted" into your source at the line where the library-command is found, so you do not need to use all $Library-commands within the first lines in your source.
However it makes sense to import libraries at the top of you listing.
Here is a short listing as an example:
$Library "MyLib"
$Library "MyLib1"
Init_Variables
Init_GUI
... main loop ...
Procedure Init_Variables
...
End Procedure
Procedure Init_GUI
...
End Procedure
Following the above code; first we import the libraries needed, then we initialise the graphical user interface (windows, menus, etc.) before the main event loop of our program starts.
Using libraries made by other GFA programmers is fine because it potentially will save you much coding-time but try to make your own as well because the benefits of using re-useable code are obvious.
Libraries made for GB32 are unique to GFA. Libraries in other programming languages take a different form: There is no facility for compiling at run time so all libraries must be compiled before they are used. Note: After a change has been made to a library you must re-compile it for the changes to be affected, plus, the source which is using the library must be closed and re-opened so that the IDE can affect the changes that were made.
Now, there is no GFA assisted debugging available for libraries; When Tron, Trace etc. do not work, debugging can become difficult. What to do?
The first step is to develop a library as a normal program and test it as running in the IDE. Let's do a simple library:
Function MyAddition(FirstVar As Long, SecondVar As Long) As Long
MyAddition = FirstVar + SecondVar
End Function
Function MySubtraction(FirstVar As Long, SecondVar As Long) As Long
MySubtraction = FirstVar - SecondVar
End Function
Let's examine this. We have two simple functions, one adds two longs and one subtracts them.
We can test this source by inserting two lines before the first function:
MsgBox MyAddition(10, 2) ' this is new line 1
MsgBox MySubtraction(10, 2) ' this is new line 2
Function MyAddition(FirstVar As Long, SecondVar As Long) As Long
MyAddition = FirstVar + SecondVar
End Function
Function MySubtraction(FirstVar As Long, SecondVar As Long) As Long
MySubtraction = FirstVar - SecondVar
End Function
Running this program brings the expected result: two messageboxes appear with the results 12 and 8.
So far, so good. As we see, our source works correctly. Now we will change this source so that it becomes a library. We replace the first two lines by two others making the source into a library module:
$Export "mylib"
$Export Function MyAddition "Adds two long-variables"
Function MyAddition(FirstVar As Long, SecondVar As Long) As Long
MyAddition = FirstVar + SecondVar
End Function
Function MySubtraction(FirstVar As Long, SecondVar As Long) As Long
MySubtraction = FirstVar - SecondVar
End Function
Take a look at those two new lines. The first one begins with $Export. This is the symbolic instruction informing the IDE that this is no longer a standard-source, this will be the source of a library.
The second line contains another $Export-command: this is the real export of the function "MyAddition" and it's like saying: "Hey, Mr. IDE, when you load this library into another source take good care and offer this function named 'MyAddition' for use!"
Now save and compile this source. The compiler will create a new compiled LG32-file in the directory where you saved this source. This is our library!
That's all there is to it.
Now let's create a new source to test the library:
$Library "first lib.lg32"
MsgBox MyAddition(10, 2)
MsgBox MySubtraction(10, 2)
First take a look at the imports-tab (right most window plane). There is the function MyAddition with a description. Do you understand where this description comes from? And do you understand why the function MySubtraction is not listed here?
When you run this, you'll get an error because the line MsgBox MySubtraction(10, 2) is not okay because the IDE does not know about the function 'MySubtraction' (do you know why?).
Comment the line out or delete it and run the source again. As expected, you earn a messagebox displaying "12".
Here is another tip shown as a sample library with a debugging-option for this library. But this means much more work for you as the programmer because of the many IF-statements you will need: (If MyDebug Then)
' Save this as "DebugSample(Src).g32" and then compile as "DebugSample(Lib).lg32
$Export
Dim MyDebug As Boolean
$Export Var MyDebug "DO NOT TOUCH"
$Export Func MyDebugOn "Enable Debugging"
$Export Func MyDebugOff "Disable debugging"
$Export Func MySampleProc "A sample procedure"
Sub Lib_Init
End Sub
Procedure MyDebugOn
MyDebug = True
End Procedure
Procedure MyDebugOff
MyDebug = False
End Procedure
Procedure MySampleProc
If MyDebug Then
MsgBox "Output any values of library-variables here"
EndIf
End Procedure
Now create a new source, you can combine the command "IsExe" with this library for debugging:
$Library "DebugSample(Lib)"
If Not IsExe Then MyDebugOn
MySampleProc
...
If the library is imported with this source it is automatically set into "debug-mode". In the compiled EXE of your source, the debugging will not work because IsExe will return False. Other ways of debugging can also be imagined. It's your choice!
Note: This way will not work with GB32 Version 2.0!
Any element defined in a library can be used in the main program provided it has been exported.
Here is the generic export-syntax:
$Export Proc|Func|Sub|Var|Const Elementname "DescriptionForElement"
Proc (or Procedure) is the export-command for a procedure.
Func (or Function) is the export-command for a Function.
Sub is the export-command for a sub. Because "Sub" usually defines an event-sub of the main-program, you should use Procedure instead of Sub when writing a procedure which is not an event-handler.
Var is the export-command for a variable (string, long, type etc., but not a constant!)
Const is the export-command for a constant (but not for a variable!)
Elementname is the name of the element you want to export. In our example above, it was "MyAddition".
DescriptionForElement is a descriptive text which can be shown in the IDE. Click with the right mouse-button onto the 'MyAddition' keyword in our test-source above. You see a tooltip which contains this text. You can create long descriptions with multiple lines by using #10 (LineFeed) in the text, e.g. Although the tooltiptext will be automatically split into multiple lines when long.
$Export Function MyAddition "Adds"#10"two"#10"long-"#10"variables"
It is also a good idea to add the parameters of a sub-routine (function or procedure) and the result (of the function) to the description. For our example, we could have written the descriptions as follows:
$Export Function MyAddition "MyAddition(FirstNo As Long, SecondNo As Long) As Long"#10"Adds"#10"two"#10"long-"#10"variables"
It is very useful to use a description for exported elements for your own use and others.
Any variable or constant can be exported for a calling programme to use. You can easily export all variables with this phrase:
$Export Var * "Exported Variables"
As you see, the asterisk "*" works for variables and means nothing else than "all variables" in this case. In fact it ("*") can be used the same as you would use 'wildcards'.
Other examples that show how;
$Export Var d* "Exported Variables that start with the char (d)"
The wildcards for $Export are similar to the long file name wildcards in Win95/98 etc.
$Export Sub *Exp* "Any sub containing Exp anywhere in the name"
$Export Sub A?B*Exp*C "Any sub starting with A, third letter B, containing Exp, and ending in C"
A variable in a library must be declared (as usual). Example:
Dim a As String
A phrase like
Dim a As String = "Hello"
however this does not allocate the string "Hello" to the var. Variables must be explicitly set at run time.
Repeat: To give a value to a variable you need to do it at run time, i.e. within a procedure or a function which must be called at some time so that the variable does receive its value.
In the previous version of this doc we said that;
However, another way is possible: GB32 offers a library-event-sub which is always "Lib_Init". This sub is automatically called when the IDE loads/parses the library.
The above statement is not correct. No procedure/function or sub is automatically called in a library.
One can use Sub Lib_Init as one place to declare constants, variables, or other elements within a library. For large libraries one will also use other procedures/subs for declaring constants etc.
With constants GB32 allows us to preset these constants immediately when defining them (which is correct; you cannot change the value of a constant after it has been declared). So this code is okay for constants:
$Export "sample"
Const a1 = "Hello"
Const b74 = 123
$Export Const a1 "A sample string"
$Export Const b74 "A sample number"
' or, instead of the two lines above:
$Export Const * "Sample constants"
Sub Lib_Init
' nothing to do here
End Sub
Below is an extract from one of my libraries - the whole library is very large so you will only see parts of it;
$Export
// EXPORT DIRECT DRAW FUNCTIONS
$Export Func DDRelease "DDRelease(lpDDO As Int) As Int"
Sub Lib_Init
' Offsets for DirectDraw7 Interface
Global Enum _
iddQueryInterface = 0, _
iddCompact = 12, _
Declare Function DirectDrawCreate Lib "ddraw.dll" _
(ByVal lpGUID As Long, ByVal lplpDD As Long, _
ByVal pUnkOuter As Long) As Long
// Export the API's
$Export Declare DirectDrawCreate
// Create GUID's
GUID CLSID_DirectDraw = {d7b70ee0-4340-11cf-b063-0020afc2cd35}
// Export the GUID's
$Export Const CLSID_DirectDraw
// ETC.
EndSub
Sub TypeDeclares()
Type PALETTEENTRY
peRed As Byte
peGreen As Byte
peBlue As Byte
peFlags As Byte
EndType
$Export Type PALETTEENTRY
// ETC.
EndSub
Sub ConstDeclares()
Global Const DDCAPS2_PUREHAL = 0x08000000
Global Const DDSCAPS3_MULTISAMPLE_MASK = 0x0000001F
// ETC.
EndSub
Sub ExportConsts()
$Export Const DDCAPS2_PUREHAL
$Export Const DDSCAPS3_MULTISAMPLE_MASK
// ETC.
EndSub
As you will see from above none of these Subs are explicitly called. GB32 deals with all declares, variables/constants/declares etc automatically.
It is sometimes necessary to export a particular Type from more than one library where these libraries are imported to the same listing. This is not a problem if the Types are defined with the same number and sized members. GB32 checks whether the Types are the same in size and number of elements. If they are not you will receive an error.
As previously mentioned, Lib_Init is not automatically called by GB32 when running the library contrary to the previous version (1.0) of this document.
Lib_Init is a good place to initialise variables etc.
Here is the prototype for the sub:
Sub Lib_Init
' Initalise your variables here!
' Do your Declare-statements here!
End Sub
To Export a Procedure or Function one uses the $Export statement as mentioned before. One can of course Export all procs/funcs/subs with the following statement;
$Export Procedure *
This exports the Lib_Init too, and any other (all) procs! It is surely better to export any procedure and any function with a single $Export-statement and add a comment to the export; we should always encourage good programming practice. Other ways to Export multiple funcs exist;
$Export Procedure f*
This will Export all funcs/procs/subs whose name begins with the character 'f' or 'F'.
Of course to explicitly Export each function name with comments will require more work, especially for big libraries with many functions and procedures, but taking shortcuts is not necessarily the best way. One should also consider other programmers; perhaps you will sell your library - in this case your client will not thank you for those shortcuts.
Use wildcards as described earlier in this document.
Declare
Using Declared API-functions is often necessary when GB32 has not included it in its repertoire. You are allowed to use the Declare-statement in a library. Like all Exported elements in a library API functions can be 'Declared' anywhere in the source.
'Elements' within a Library are private unless Exported, this rule is always in effect.
Using Declare for system calls (API's) merits special attention: some parameters are ByVal while others can be made ByRef. You need good Windows-documentation to find out when to use each. Essentially GB32's own method is to use ByVal but you may prefer ByRef for some cases.
Own functions and procedures
As written before, you can export any own function and any own procedure from a library. If you do not export one explicitly it is not exported. This is logical, it is the right way to distinguish between private procedures/functions and exported procedures/functions in your library.
In addition to the tooltiptext option by placing a comment behind the Exported function -
$Export Function MyAddition "(FirstNo As Long, SecondNo As Long) As Long"#10"Adds"#10"two"#10"long-"#10"variables"
another feature using Htm files can be used allowing extensive help for users. An example from GFA is included illustrating these benefits.
Make a folder somewhere and un-zip the file Shfolderlib.zip into that folder.
You should now have two new folders within the one you have made -
Folders - compiled and src
and a GB32 listing called test_shfolder_lib.g32
Load the file test_shfolder_lib.g32 into the IDE and take a look at the import section (right most window plane) and you can see all the imports.
At the very bottom is the imported Declared API SHGetFolderPath() with a decription. You will see the description as "Takes the CSIDL of a folder and returns the pathname". Right mouse click on the API call in the listing (line 84) and you will see the tooltiptext.
Now, with the mouse over the API call left click and then press F1. Magic, you should have the Htm file "SHGetFolderPath.htm" loaded into your browser. Take a look in the folder src and load 'ShFolder.lib.g32' The $Export for this declared API is as follows;
$Export Declare SHGetFolderPath "Takes the CSIDL of a folder and returns the pathname"#10">SHGetFolderPath.htm"
Note that the '>SHGetFolderPath.htm' has been added to the comments section of the $Export comment.
To re-load all libraries after making a change to one of them instead of closing and re-loading the source code. (Suggest a function key)
The "Load error for library" could bring a valid reason why loading has failed.
Allow 'Data' statements in Libraries. Allow ':Files' in Libraries.
Here is a copyright-free prototype for libraries with GB32. Use this to get a frame application for a new library you want to create.
' GB32-program:
' Type: library (LG32)
' Version:
' Author:
' History:
' To-Do:
$Export "YourInternalLibraryName"
$Export Func AnyFunctionNameToExport "(Parameter,RetVal)"#10"Description"
$Export Proc AnyProcedureNameToExport "Description"
$Export Var * "DO NOT OVERWRITE"
$Export Const AnyConstantNameToExport "Description"
' Dim VarName As VarType
' Const ConstName = ConstValue
Sub Lib_Init
' Do os-function-imports here:
' Declare...
End Sub
Sub Declare_Types
' Do os-function-imports here:
' Declare...
End Sub
Procedure Dummy ' prevent interpreter from eating up the last lines
'
'
End Procedure
'
'--
(You can use the Editor Extension "jht" (Joe Hurst Tools") to create such frame applications for libraries, editor-extensions and standard sources with only two mouseclicks!)
Any contribution is welcome!
Do you have more ideas for this paper?
Do you know more errors? Bugs?
Do you have special problems (which could be of public interest)?
Don't be shy, contact me and let's see what could be done!
You can contact Joe Hurst via his website at http://www.jhurst.de or directly via e-mail at info@jhurst.de.
You can contact John Findlay via e-mail directly at John's e-mail address John.Findlay1@BTInternet.com