CSDA Tips on
Alpha Five &
Product Tips & Tricks

Alpha Software VAR logo
Last Updated 20 July 2013
Copyright © 2012, Computer Systems Design & Associates, All rights reserved
(linking to this page is OK, posting contents of this page in whole or part is not OK)

(add your thumbs up to
Google if you like this page)
(add your facebook like)

If any of these tips are particularly useful to you, or make a big difference in your Alpha Five application, or you believe any aspect of what is stated is incorrect, we'd love to hear your comments!
Just email us at support@csda1.com
or click the comments link next to each tip

Any database program, Alpha Five included, is very complex in what it does and how it does it. Database programs push the hardware, software and networks much harder than almost every other typical programs people use, like Microsoft Word, Excel or browsers. So, while developing in Alpha Five is typically much faster, there are still things that are very useful to know when developing a database program so that it runs efficiently, with few errors, and is easy to maintain.

Below are tips and tricks on a variety of topics (many of which apply to any database) that you should be aware of as you develop an application in Alpha Five.

Re-read each tip very carefully for it's meaning and guidelines. And if this is just TMI (Too Much Information) for you to absorb, we provide consulting, mentoring and training to help you through these issues.

CSDA Code Utility for Alpha Five specific tips

  1. Speed up starting an A5 Desktop with a Database shortcut
  2. Menus are too complicated or too large
  3. Customize Menus for a specific database
  4. Restoring messed up button files
  5. Lost customized button
  6. Accurate Timing of code
  7. Quick Backup to send files
  8. Modeless Interactive Window
Alpha Five General Tips & Information
The Short List(What to do without the details!) Updated 26 Apr 2011

Otherwise, click below:

  1. Use functions, Not scripts Updated 28 Mar 2011
  2. Code Editor, Function & Variable Limits Updated 1 July 2013
  3. Choosing the right & fast function
  4. Alpha Five Data Dictionary and XBasic Data Types Updated 31 Mar 2011
  5. Alpha Five File Types Updated 25 Apr 2012
  6. Variable usage, dimensioning and deletion Updated 07 Nov 2011
  7. Field Rule Recalcs Updated 17 Mar 2012
  8. Delimiter characters for CSV Updated 28 Mar 2011
  9. Files over 2 Gigabytes
  10. Why Network Optimization (shadowing) does not necessarily improve speed Updated 29 Mar 2012
  11. Lightning Query Optimization (LQO)
  12. Record Locking 101 (and how to speed up database usage including Forms/Reports/Appends/Updates) Updated 13 Sep 2011
  13. Coding with XBasic versus Action Scripts Updated 21 Mar 2010
  14. AEX Compiled libraries
  15. Coding Tips Updated 07 Nov 2011
  16. Network Issues related to speed Updated 16 Apr 2012
  17. Speeding up code Updated 12 Mar 2011
  18. Isolate Code that depends on tricks Updated 03 Jan 2010
  19. Protecting your code Updated 09 Jan 2012
  20. Using Active-X objects tips Updated 27 Feb 2010
  21. Bitwise Logical operations (Binary) with numbers Updated 12 Jun 2012
  22. Alpha Five hardware suggestions Updated 15 Aug 2011
  23. Alpha Five Data & Index Corruptions Updated 26 Aug 2011
  24. Compatible DBF/Index Naming usage Updated 09 Nov 2011
  25. Paths in Alpha Five Updated 25 Apr 2012
  26. Variable & Code Naming warnings Updated 04 Feb 2013
  27. Regular Expressions for Alpha Five Notes Updated 11 Sep 2011
  28. UI_Msg_Box Codes  Updated 02 Oct 2011
  29. Good Alpha Five Built-in Functions Updated 22 Apr 2012
  30. Data Security Tips Updated 17 Dec 2011
  31. Alpha Five/QReportBuilder Version Release Dates Updated 20 Jul 2013
  32.  Using Dates in a database Updated 06 Apr 2012
  33.  Windows 7 (and Vista) compatibility Updated 09 Feb 2013
  34. Theory of Deduping data Updated 11 Apr 2013
  35. Easier Customer Support Techniques Updated 01 May 2013
Links to Other Alpha Five Tips
  1. AIMS Web site - Alpha Five naming conventions
  2. AlphaToGo - various Alpha Five Articles
  3. LearnAlpha.com - various Alpha Five articles
  4. Proctor & Peake - various Alpha Five tips
  5. Marcel Kollenaar's tips on using DLL's with Alpha Five
  6. Dan Blank's Tips
  7. QODBC - QuickBook interface to Alpha Five tip
Alpha Five Error Reporting
CSDA DiagInfo for Alpha Five (Free)
Diagnostic Information reporting Utility

This Alpha Five add-on utility (which also comes with all CSDA products) is available with free usage for all, except it may not be distributed with any software package or linked to the downloadable file on the web without permission or license from CSDA.
Click here to go to the main page to download

When you encounter errors, problems or have wishlist items in Alpha Five, one of the most important thing is to get a system snapshot of the hardware & software and the configurations of the operating system & Alpha Five. It is also important to categorize the problem or request. If you've used Alpha Five's built-in bug reporting email, you will see it reports, at most, the version and build numbers, and optionally can attach a copy of the database, a very limited ability. The CSDA DiagInfo can be used to send info to CSDA, Alpha Software or other support so hopefully they will have a full picture of your working environment.

Customized versions of CSDA DiagInfo are available for developers. Additional features are built into the commercial version. Reporting info can be added to or deleted for individual developer needs.

CSDA Code Utility Tips

To speed up starting, create a Desktop Restore shortcut for each specific database you use.
When you start with the A5vX Desktop Restore, or the A5vX Last Desktop restore desktop icon shortcuts, it starts a special database and then starts the actual database. Just start your database, then select the CSDA Code Utility "Create Shortcut" button. Press OK, and a new A5 Desktop Restore specific to the database will be placed on your desktop.

Menus are too complicated or too large or have features I never use
Simply edit the dialog box menu for the appropriate dialog type (Rolldown, Rollup, Minimize, Horizontal dock or Vertical dock) and remove buttons you want. Do this by pressing the CSDA Code Utility "Help" button. At the bottom are various edit buttons for the dialog boxes. Just press one you want to edit, scroll down to the button lines (near the bottom) and "comment out" any button you don't want to use by simply putting a single quote at the beginning of the line to eliminate the button. If you made a mistake, removing the single quote will restore the button. After you made desired changes save the file and exit. Then use any utility's restart buttons to restart the utility with the new menus.

In a similar fashion you can also add buttons (both predefined and ones that run your own code) to the menus. Just read the comments at the top of the button files and copy similar buttons as a guide and modify.

Can I customize Menus for a specific database?
Yes, you can. This is sometimes referred to as "skinning". Simply select the "Edit A5 Buttons Loc" on the Utility's help button, and when done, save to the database directory as a file name "databasename.abt" (Be careful, as Notepad will try to save it to "databasename.abt.txt". Rename it if this happens). This file has lines that point to where the various other button files are located, and they could be anywhere in the system, but typically should be in the default Alpha Five "application" folder, or the database directory.

Restoring Button files
If you have a problem with the CSDA Code Utility button files that are not operating properly after changing them, you can restore the original ones by either

Lost customized button
You lost your customized button code for the CSDA Code Utility after reinstalling or updating the utility. Because the button files are subject to change from version to version (although they haven't in a long time), the new versions always use the current default definitions. However, all of your old button files are renamed with a date at the end of the file name.

You can either edit the new ones copying in your older changes, copy the old ones to the newer ones (with the risk of a format change), or use a button Location file skin to redirect it to some custom named files that will not be overwritten.
Quick Backup to send files
When you need to send database files to others (or post on the message board), here is a quick way with the CSDA Code Utility:
This will copy your database, dictionaries and web components, compact (Pack) them, and then zip them together into a file of your choosing.
Accurate Timing of code
To test code speed, using the CSDA Code Utility, select a code editor tab and press the Code timing button and that function or script speed will be tested. It is generally best not to have any dialog boxes pop up in the code that require user responses, as the user response time will be included in the timing results.

If you are on the Interactive Window (IW) tab, just highlight (select) some code, and press the Code timing button, and that code will be timed. It is often best to initialize all needed variables in the IW, and then test the key code separately.

Results may vary up to about 3%, depending upon what windows and Alpha is doing at the same time. If you run the tests at different times where the Windows environment and available memory has changed, your results may vary. Running different tests right after each other yields a more accurate comparative timing value.

Modeless Interactive Window
Sometimes you need an interactive window in a modeless dialog, that you can run code without changing the context of the primary window. By using the Code Edit button to edit any existing code, you get a modeless script editor and an interactive window mode on the 2nd tab. This allows you to probe variables and run test code without changing the context of the other windows.

Alpha Five General Tips & Info

The Short List (What to do without the details!) (Email Comments) Updated 26 Apr 2011
Use a function, not a script (Email Comments) Updated 28 Mar 2011
When you create code in Alpha, particularly using Alpha Five Genies, or Action Scripts, you are creating code as a script. It may be embedded on a form, or available as a Global Script, but the result is the same.

Move the code off the form (layouts)
The 1st point is to change your code to a global script or function, rather than embedding it on a form (or other layout - I'll refer to them all as forms for this tip).

When you place the code directly on the form, you typically have access to all the variables and fields available on the form. In other words it typically "inherits" the variable space of the object it is embedded in. In addition, "parentform" is an alias to the current form's name. If you change the name of the form and use the alias as a pointer in the code, the code still works. The code is being executed in the context of the form, so has access to all of those properties, as well as all variables that the form has access to.

However, if you move the code off the form and make it a global script or function, you may lose the context of the form (which can be totally eliminated by passing the form's pointer). Recent testing in A5V5 and up seems that this is not the case. Parentform and Parent do seem to work in the context of the function. However local variables on the form are accessible only if you pass the pointer to the function, or retrieve the form's local variable space by using the parentform or parent alias to retrieve the pointer to the form's local variable space.

You get these advantages with Global Scripts & Functions;
Using Script_Play_Local("script_name") allows the global script code to inherit the space from where it is used, so all local variables of the form are also available (and changeable) within the script, which simplifies usage for beginners.

But this can be also be bad -- VERY BAD! Even if you use SCRIPT_PLAY("script_name"), you still have access to other global or shared level variable names that have been defined by Alpha, or yourself, that you did not know were being used. This can cause your's or Alpha's code portions to fail, because of different uses of the same name. See this tip Variable usage, dimensioning and deletion.

Use functions, Not scripts!
I've seen people use 100's of scripts to accomplish the same as 1 simple function could do. The problem with the function is that, in general, if you don't use a passed pointer (e.g. local_variables() etc.), the function does not know the context of the code that called it. No local variables from the calling code (where the function is referenced) are available.

Normally this is good for debugging, and the context is not needed. If it does need a context, e.g. it prints a report of an ID that is in the current layout, then you can pass the ID value as a parameter. If you need many variables, you can pass a pointer to the local variables, a pointer to the layout, or to just your own pointer with lots of property values.

To help isolate name usage, change the script code to a global function. A global function can be created as simple as

FUNCTION script_name as V ( )
' original script code here

Then just use script_name() instead of SCRIPT_PLAY("script_name") wherever you need that code to be placed. Also, make sure each and every variable used is dimensioned (prior to the code's 1st use) with DIM (or is in the function parameter definition line)See this tip for a discussion of variable dimensioning.

If you do need the context of the form (layout), in a function, pass a pointer to the context a parameter, such as

FUNCTION script_name as V (layoutpointer as P)

' in the use of the function, pass the layout pointer, as in
'    script_name(parentform.this)

with layoutpointer
    ' original script code here, has access and can change all of layout's variables
    ' (which can be bad, so use this method less)
end with

' Or prefix layout' variable name with pointer (better method)


However, the function templates shown above are really not the best way to use a function, but will allow you to quickly move your script code into functions. The next step is eliminate passing the local_variables() pointer, and instead pass just the parameters you need, combined with a returned value. Here is an example;

FUNCTION SumNumbers as N (Number1 as N, Number2 as N)

' To use this function, use a line similar to
'    sum_of_the_numbers = SumNumbers(1.5, 3.4)
' or put any valid numeric value or numeric expression in the parameters, such as
'    sum_of_the_numbers = SumNumbers(numeric_variable1 *1.5, numeric_variable2)

' All of these are dimensioned in the function definition line, so they are already DIM'ed
SumNumbers = Number1 + Number2


The key advantages to a Function over a script are

simplifying the actual flow of code and yielding greater comprehension of what the code is doing. What the function does inside may be complex, but using it is simple.

My development library has over 1000 finished functions I have developed over time for many purposes, and exactly 1 finished script (the AUTOEXEC script). 'Nuff said?  :?)

Code Editor, Function & Variable Limits (Email Comments) Updated 1 July 2013

There is no practical limit to the # of code library objects (Scripts, Functions, Menus, Toolbars, Classes & Enumerations, import operations).  The only limitation is the maximum size of any single file (2 GB windows limitation) and the amount of main memory available to Alpha Five (currently 3 GB max as Alpha Five is a 32 bit program).  The same would also hold for data dictionary items (Operations, Forms, Browses and other layouts).  There are ways to get around this limitation, but no Alpha Five user has ever gotten close (including Alpha Software). There is an additional limitation of any data dictionary memo file (e.g. adm, sem, ddm) or memo fields file (fpt) of 10000000000 entries, so even with multiple edits or changes (and it saves every previous copy that was smaller by 512 byte sizes), exceeding this number is probably not possible.  Compacting the files eliminates all the old entries.

In some versions of Alpha Five, there is a limit of functions and scripts that can be exported or imported, but is always at least 1000 objects of the same type.  

Function Parameter Limits
Functions can have a maximum of 20 parameters - If you need more than this, you should probably pass some or all of the parameters with a pointer that has properties, or as an array of values. You can also pass the local space variables pointer (Use LOCAL_VARIABLES() to get local pointer in the function usage - not in it's definition line, where the function is used). Addin, Global and Session (session means shared) variables pointers can be obtained while within the function with ADDIN.VARIABLES(), GLOBAL_VARIABLES() and SESSION_VARIABLES(), and do not need to be a parameter. To illustrate some of these:

dim a1 as n
dim b1 as n
dim c1 as n
dim d1 as n
dim e1 as n
ui_msg_box("Final Result","YourFunction returns "+ltrim(str(e1)))

' Note that the local_variables() used in the function definition line below
' only is used if parm3 is not specified when the function is used above. 
' So if the 3rd parameter is specified with local_variables(),
' it uses the variable space of where the function is used. 
' If not specified, it uses the local space of the function itself
FUNCTION YourFunction as N (parm1=0 as n,parm2=0 as n, parm3=local_variables() as p)

' Initial return value

' Add in parm1 value

' Add in parm2 value

' Add in calling functions b1 value using passed local variable pointer as prefix pointer

' another way to access the calling local variable sapce
' Set default pointer space to parm3
' (this pointer space will be tried 1st, then this function's local variable space,
'    then shared variable space, then global variable space.  Anything new DIMed between WITH and END WITH
'    will be in the variable space parm3
with parm3
end with


Other Function Limits
Length of function definition line is at least 1200 characters but may be unlimited.

Internal (sometimes referred to as local function) Function names (functions defined within a global script or global function) should not begin with the same characters as the Global Function or Script Name, as earlier versions of Alpha (version 9 and earlier) gets confused by them.

Code Editor Limits
Code Editor can have maximum of 30 functions or scripts open at a time.

Each code editor tab has a limit to it's size, primarily based upon a size where CRLF count as 1 character. For typical code, it is is about the number of short lines/4 for that version.

When you close the code tab and try to reload, the text at the end is truncated if it exceeds the limit.

A5v5    10915 short lines,   65323 blank lines, 130641 bytes,
1554 Maximum line length (before trailing comment - may be a compile limitation)
A5v6    50417 short lines, 196120 blank lines, 392351 bytes
A5v7    50417 short lines, 196120 blank lines, 392351 bytes
A5v8    50417 short lines, 196109 blank lines, 392351 bytes,
No maximum line length (at least >6500)
A5v9    99472 short lines, 392320 blank lines, 784750 bytes
A5v10  99472 short lines, 392320 blank lines, 784750 bytes

If you are using an Action Script "Inline XBasic" action line, it's code may have limitations as well. If you save and close the action script for editing, then reopen it, you may find the Inline XBasic code was truncated.

Variable Limits
There is no practical limit to the number of variables and/or properties (dimensioned at any scope), nor the size of any data.

A 100 Megabyte variable (string or blob) should not be any problem. However, should that variable be passed to a function (by value), then a copy of it's value will be placed on a stack (a temporary place). And this occurs for each level if functions are nested and you are passing the parameter. If the value is returned, another copy is also placed on the stack. If many copies are made of the large variable, you may get an Alpha Five "OUT OF MEMORY" error, which has been seen with 350 Megabyte variables.

In these cases, it may be best to pass the parameter by reference, or place the variable as a property of a pointer (which can just be the local_variables() pointer) and pass the pointer.  This will save memory space and save the time it takes to make a copy (which is not insignificant for large sizes, say bigger than 1 Megabyte if used a lot).

Also, for large character or blob strings, setting them to a length of 0 after usage will return that space to the available memory afterwards. Deleted variables will also return the memory space. This happens to local variables when a script or function is closed or shared(session) variables when a session (typically an open layout such as a form or browse) is closed.

Arrays can have a maximum of 127 dimensions (although doubtful anyone would use more than 2 in a normal basis).

In practical usage, it seems as when variables typed as a Collection exceed something on the order of 5000 elements, there seems to be unexplained crashes in A5 (tested on A5V8), so beware if you have large collections.
Alpha Five Data Dictionary and XBasic Data Types (Email Comments) Updated 31 Mar 2011

Variable types allowed in XBasic Code
Types actually stored in a DBF Table Structure

Non-Memo data field types (as stored in the Database structure)
Memo field data types (as stored in the Database structure)
Memo field data types (as stored in the Data Dictionaries)

Alpha Five File Types (Email Comments) Updated 25 Apr 2012

Alpha Fives stores data in the native format of DBase III. It actually is closer to the FoxPro format for dBase files, but actually is enhanced beyond that allowing longer field and index names, as well as supporting more data types than many other dBase compatible applications.

This can cause problems with other applications that use the format.  They will see only 10 characters for the field names and index names.  Unsupported data types may either be ignored, interpreted as a different type (typically character) or cause a program error/crash.

File and record locking may or may not be compatible with other applications, meaning other applications should work with a copy of the data, or by opening the data files exclusively

Alpha Five File type extensions
Other file extensions used by Alpha Five (partial list)
Industry Standard file extensions used by Alpha Five (partial list)

Choosing the right Alpha Five Function (Email Comments)
Alpha Five has over 10,000 functions and methods. Methods are basically functions associated with an object like forms, browses, email etc, e.g. form.view(form_name), and can be considered to be a function. How do you choose one function (or method) over another? The main criteria are
  1. It has some capability that another one doesn't have, or
  2. It is clearer as to what you are trying to do so that others viewing the code (including yourself 6 months down the road) could understand it, or
  3. It is more modifiable for future changes, or
  4. It is faster
All of the above are important in selection of a function, but the one that you can have the most control of is speed. No matter how big databases are, they will grow over time to a point where speed will become important. No one usually cares if something takes 2 sec or 5 sec, and sometimes 1 min or 5 minutes, but running a report/operation/etc. that takes 10 minutes vs 1 hour is significant (especially if you need an update every 15 minutes!). (Note: No matter how long something takes, once it exceeds a second or 2, you should display the status or at least a warning that the process will take a time to complete)

In terms of speed, Alpha Five functions (and methods) seem to fall into 4 basic groups
  1. Key Functions - Provided by Alpha, written in C or those that are in optimized function libraries that run very fast for what they do
  2. Key Xbasic Functions - Those written in XBasic code, but the majority of the operational code's time is spent using those functions of #1. They are typically slower than those written using techniques of #1 and could be user-written. These could also include XBasic wrapper code (See #4 below for definition) calling Windows API, OLE or Active X functions
  3. Xbasic functions - Those written in XBasic, where most of the time is spent in Xbasic code - typically much slower than #1 or #2, could be user-written
  4. Wrapper functions - These are XBasic functions that call other functions of type 1, 2 or 3 and just rearrange or simplify the input parameters and then return values from a function for easier use
How can you tell the difference?

One way is to use the CSDA Code Utility's accurate timer to test samples of code or entire functions and scripts. This utility basically loops code many times to arrive at an accurate timing value.

Those in #1 or #2 are generally faster by a factor of 3 to 10 than the equivalent in #3 and #4. Type #4 tends to be just a little slower than the equivalent of type #3.

Also, those in type #1 and #2 typically have simpler types of parameters and inputs than those of #3 and #4. Also they may have less error checking of parameter values (e.g. a numeric parameter less than 0 may fail) and less error error trapping than #3 or #4.

Occasionally, Alpha Software changes a function of type #3 or #4 to a type #1 because they need it for internal uses or because enough developers seem to need that speed, so newer versions and releases may be faster. Re-testing is sometimes required to ascertain this. For code that operates on multi-Alpha Five versions, you can occasionally benefit by using version dependent code.

So in general, stick to types #1 and #2 if it makes no difference. Use type #3 and #4 if speed is not important. If you need a type #3 or #4 and speed is important, either
  1. Write an equivalent function for your use that might be faster (I've done this many times), or
  2. Create a function specifically tailored to the usage without any bells and whistles (features), or
  3. Embed the equivalent code in each and every place you need to use that code that needs the speed.
The last affects readability, but can speed things quite a bit, particularly where the function's main code execution approaches the speed of the overhead of the function call.

A new feature (Called Code Count) to be released in an upcoming CSDA Code Utility update will do counts of various scripts and function usages in your databases, and will indicate if the code is user XBasic, Alpha Five XBasic or Alpha Five internal code, and whether there is any documentation on that function, a useful starting point.

Variable Usage, Dimensioning & Deletion (Email Comments) Updated 07 November 2011

Alpha Software's webhelp also has some additional information here: Variable Scope

Variable spaces:
A variable space is one or more variables of any type grouped such that a reference to just the variable name will use that name in that variable. Alpha has a number of built-in variable spaces, which the main ones are Addin, Global, Shared (sometimes referred to as session), Layout (which is just a subset of Shared) and Local.

The main difference for Shared versus Layout variables is that Layout variables are only accessible from code in the current form, and not embedded forms or other embedded objects. Shared variables are accessible to any object within the current layout window.

Variable space persistence:

If not explicitly DIM'ed or previously used in the current code, Alpha will always look for a referenced variable name 1st in the Local space, followed by Shared, then Global for a matching variable name, and fail if it doesn't find it. If not previously DIM'ed or assigned a value in the current code, then a new value will be placed in the local variable space

Basic guideline rules for variable usage are: Dimensioning styles
There are 2 styles typically used for dimensioning variables. They are
Dimension as Local
If a variable is not DIM'd, and you don't reference an explicit variable space (addin, global, shared and local), the current most limited space (in Global, Shared or Local) that is DIM'd is used for references and assignments.

If not DIM'd at any level, then there is implicit DIM as a local variable (or at the space as defined by the WITH/END WITH command directive) upon 1st use.

Note that as of Jan 1, 2010 an explicit "local" DIM, as in "DIM LOCAL varname AS type" does not work. So to dimension at the local variable space (or at the space as defined by the WITH/END WITH command directive), you must do a "DIM varname AS type".

In a similar way DELETE LOCAL varname does nothing, but DELETE varname deletes the lowest level space defined


If you use a property pointer, it will use the scope of that pointer. So you could force a local DIM or delete by doing something similar to (substituting the variable name for varname and the type below);
dim localptr as P
DIM localptr.varname as type        ' for each DIM needed at the local level
DELETE localptr.varname              ' for each DELETE needed at the local level

You could do similar things at other levels with
Cautions using SCRIPT_PLAY_LOCAL()
Dimensioning variables as local helps eliminate un-intended side effects, however, if you create scripts (action or XBasic) and use SCRIPT_PLAY_LOCAL() to execute it, it operates in the same variable space as the place it was invoked from.

For your code, you know what variables you are using and can avoid naming reuse.

However, Alpha Five uses many cases of SCRIPT_PLAY_LOCAL() in it's code generated by Genies and Action Scripts, so there is a potential (although relatively small chance) of their being a conflict of names between what you use and what Alpha generates at the local level.

Xdialog Variable Spaces (Updated 30 Mar 2010)
XDialogs inherit the local variable space from the code they were started from. This means any variables dimensioned locally prior to starting the XDialog will be available in the XDialog. And of course, as before, any references to variables that are not found dimensioned locally, will be checked in the shared variables and then the global variables.

Some of the controls in XDialogs, e.g. ListView objects, use an Active X Control (another program essentially). They are implemented in ways that require some variables to be dimensioned at the shared (session) level, as those Listview events are running in a different variable space and must have access and set some values from the Xdialog variable spaces.

Minimize Variable Name collisions with code not your own
If you need to prevent conflicts (collisions) with creation and deletion of variables with the same name at different scopes (particularly at the shared, global or addin scopes), by code that is not your own (e.g. Alpha Five built-in functions, code generated by action scripts and genies) create a uniquely named variable space pointer (possibly with a GUID or time stamp in the name), and reasonably long to be unique to a maximum length of 23 characters (Alpha can use 24, but has had some places that only saw the 1st 23) and then use that pointer to create property values (variables) for all the ones required at that scope.

That's what is done in the CSDA Code Utility. Version 2.69 and after, creates 1 global and 1 addin pointers and no other top level variables in the entire program. The name is chosen to be long and not common to eliminate possible conflicts with other programs. Many variables that are needed by many aspects of the program are stored under those pointers as properties.

Why you shouldn't delete a variable, nor have to
The reason for not deleting a variable, and for that matter using a variable from any other session (also referenced as the words process or thread in Alpha Five - a layout like a form or browse always runs in it's own session), is that it has an independent purpose and timing.

In most cases, when you create a variable, you intend on using it and for that particular variable type. Its use should have a determined purpose, and does not need to be redefined to another type. It would be better to create another variable of the type needed, e.g. tmpC, tmpN, tmpL etc. for Character, Numeric, Logical etc.

E.g. two forms create a global variable "tmp". One set's it to "text", the other deletes it and sets it to the number 2. This will cause an unintended side effect (read as failure) in the other form. Just changing the value alone will cause the other form to fail what it was doing, unless you intend it for use as an inter-thread communications. In that case usage must done in a way that allows for proper operation, and that one process does not receive a partial message (A whole other subject to discuss!)

When a session finishes, all the variables at the session (shared) scope and local scope are released (deleted). Similarly, when a function or normal script terminates, the local variables for it are automatically deleted.

For Global or Addin variables, they persist. Global variables remain until you close the database, Addin variables remain until you close Alpha Five (even if you open another database). If you must delete the global or addin variable, try to do it in the same session that created it (and the same applies to variable writes, although it is generally OK to read from elsewhere). If not, try to make sure, by program design, any other session that writes or deletes it, is not executing at the same time.

If you need to change variable types, e.g Character and Numeric, the only real place I've seen a true need for this, is in a function's return value where the return value is type A (can be any type).
E.g. MAX() function works with numbers, characters, dates, and the return value is the same type as the inputs.
You might start with an initial value of -1 to indicate error, but for some cases, you might be returning other types. If the function code works in a way that you only need to set the return variable once for any particular exit, then you can set it for the 1st time for that exit path, and not have to delete the previous usage at all, as there wouldn't be any

Issues with Action scripts using DELETE
Alpha Five action scripts don't assume any knowledge of other code that's been used previously (including the same action script action used on another line) and thus deletes the variable to make sure there is no conflict. This carries the risk of deleting another variable at a more "global" scope that might have an identical name (unlikely for Alpha script naming, but it could happen) and being currently run. It could also delete a variable that you named that had the same name. Making sure your code dimensions everything as local eliminates this issue. If a variable can not be dimensioned as local, make sure it has a unique name.

Special Cases for using DELETE
You might be able to come up with some code examples that do need to delete variables, but those truly requiring it are going to be very unique in their purpose.

One example of this is the Code operations of the CSDA Code Utility. This has the ability to run other people's code for testing purposes and can't know what the potential return values types are going to be. Thus, there is exactly 1 delete of a variable in the code. And since it's a "local" variable, the code that created it is the same as the one that is deleting it, a safe operation.

Referencing Variables at other levels
Even if variables with the same name are defined at more than 1 level, you can reference these higher scoped variables, and test for their presence.

' They can also be referenced by creating a pointer
dim localptr as p
dim shareptr as p
dim globalptr as p



' They can also be referenced directly by prefixing the function on the property name

' Above does not work with A5 directives (commands) like DIM, DELETE, so do it this way
DIM GLOBAL varname as C
DIM SHARED varname as C
DIM varname as C   ' local DIM
' or
DIM globalptr.varname as C
DIM shareptr.varname as C
DIM localptr.varname as C
' for DELETE
DELETE, so do it this way
DELETE GLOBAL varname as C
DELETE SHARED varname as C
DELETE varname   ' deletes lowest currently defined scope of varname
' or
DELETE globalptr.varname
DELETE shareptr.varname
DELETE localptr.varname

If you want to test for a scope of a variable, these examples will return logical values

Don't use Field Rule recalcs (Email Comments) Updated 17 Mar 2012
The recalc of field rules applies to all field rules, not just specific ones of interest. It's an all or nothing proposition (for the most part). In addition, a recalc requires exclusive use for it to work, so if any one else has the table open, it will fail

Suppose you have a field rule with a field with a default value of DATE() holding the record create date (some of these might be blank from records imported). Another field has a calculated field of DATE() holding the date record was last modified (by a human, not a program).

If you do a recalc of the field rules, the blank create dates might be set to the current date, and the last modified date will be changed to the current date, not what was necessarily intended.

The field rules are intended as a set of rules to be applied while entering and changing data by human input (exception probably being auto-increment). The rules are applied to the record based upon the current values of the database at the time of entry or change.

If you have any field rule that depends upon date or time, or the current state of other records at the time, then a recalc of field rules totally invalidates the conditions that they were when they were created originally.

If you must perform an operation like the field rules, then run a global update or other operations that take in consideration of what must be done to get the correct results.

In the above example you, would not global update the create date, and may or may not global update the last modified date, depending upon intended results.

Also, using variables in a field rule is generally a bad idea. If the variable is undefined or uninitialized, the field rule will not work. The typical way to initialize a variable is via an Autoexec script or in a form or browse layout's initialization event code. This does not happen if you open the table without the autoexec, or open a form (e.g. the default form) where there is no initialization event code.

So, to sum up, If you look at the descriptions of the general method and table method of recalc and this link, it is highly probably that they are using one of those functions to perform the recalc. Based upon the documentation of the link, it implies a wide variety of effects besides calc fields. Without testing intensively, it might be hard to prove that it doesn't affect other rules, although proving it does affect other rules means finding just 1 or more, which might come out in testing quite quickly.

Based upon the documentation of the functions and my link and that there seem to be no other functions applicable, I am guessing that recalc of calc fields is really recalc of field rules. This also agrees with historical history of the recalc process as well.

Setting create/change dates and times

Setting a creation date/time into a data field is best handled as a field rule default value that is set "at start of data entry" - that way no update action will change it.

Setting a last modified (change) date/time into a data field is best handled with a CanSave Record action (in the field rule table events) so that no update action will affect it. However, any xbasic code must specifically update the change date/time field(s) when needed since the Table CanSave field rule event isn't triggered when adding/editing records with xbasic. So the Xbasic code might need to include something like this:

table_pointer = table.current()
table_pointer.change_dtf = date()
table_pointer.change_tmf = left( time(), 5 )

Delimiter characters for CSV (character separated values) (Email Comments) Updated 28 Mar 2011
Normal choices for CSV lists are comma, Carriage Return/Line Feed, or Tabs. Vertical bar ("|"), space, colon and semi-colons are also used often. But whatever character (or character string) you choose, there is a chance the user could have text with that character.

But since Alpha can have any value from ascii 1 to ascii 255 (chr(1) -> chr(255)) in a text string, use a chr(x) as the delimiter, where x is a value between 1 and 31 with the exceptions of 7 (beep), 8 (backspace), 9 (tab), 10 (line feed), 12 (Form Feed), 13 (Carriage return), and 27 (escape). Alpha Five also uses values 1 (Start of Header), 2 (Start of Text), and 11 (Vertical Tab) in some of their code, so avoid these as well.

The value 28 is a good choice, as it is not common to find a binary "00011100" in text or binary files, but any of the unused values from 17 up to 30 (don't use 31, two many consequtive ones in the binary) are good as well. In most cases, set a variable name, such as chr28 to the value, as in chr28=CHR(28) or CSVchr=chr(28). Now use the variable name, which allows you to change the CSV character to another value quickly without having to edit all instances where it is used.

If you don't have characters greater than ASCII 127, then you could also choose ASCII values from 128 to 255, although these are not as portable in other languages, so these are not recommended.

To create a string with your new delimiter, it can sometimes create extra complexity, but here are some examples with lookups which are generic, but all end up as the same return expression.

What to do if your database files are 2 Gigabytes or larger (Email Comments)
If you have a database data file (*.dbf) or memo field (*.fpt) that is approaching the 2 gigabyte maximum allowed size for these formats, you must get all files under the 2 Gigabyte limit.

The solution is to segment each memo field of the table into separate tables with links back to the main table holding all the other non-memo fields. If you are exceeding 2 Gigabytes with the main dbf file, then separate fields into more than 1 table with a link.

Alternatively, use an SQL database if you are able to.

Why Network Optimization (shadowing) does not necessarily improve speed (Email Comments)
Network Optimization (shadowed) makes a copy of all layouts (such as forms, browses, reports), operations (like exports) and user global code, toolbars and menus to a faster location (typically a local drive) in attempt to minimize network traffic of these. Data is still accessed from the "server".

If the shadowed copy is located on the same drive as the database application, it serves no purpose, as it still needs to read it from the same drive (and presumely wouldn't be any faster).

However. only if more than one system's access of layouts, operations or code interferes with the network traffic of another, does the network optimization become useful from a global network performance of all systems. Any local increase in speed is seen only for those applications that open forms, browses and other layouts, or run Alpha operations repeatedly, or use global code entries.

A "compiled" AEX file, stores your code entries, toolbars and menus.  They can be loaded automatically from the database folder if it has the same name as the database, or from the addins_installed folder located in the Alpha Five exe folder, which is generally on the local drive.  It can also be loaded via XBasic commands as well.  In any of these cases, the compiled code is loaded once, and then all of it's contents are stored in main memory. If using a shadowed database, it will be loaded from the shadow directory if it has the same name as the database.  So to minimize network overhead, it is better to be located on the local drive.

Lightning Query Optimization (LQO) (Email Comments) Updated 29 Mar 2012

Lightning Query Optimization (LQO) is a process of Alpha Five that happens automatically whenever you apply any filter or query to a DBF table (except for the filter inside an index definition). It speeds up query time by creating a dynamic range to an index.

Back in Alpha Four, (and actually is still doable in Alpha Five), you might do a search (query) by setting a range that reduced the number of records that needed to be searched.

E.g. If you wanted only people whose last name began with S, your range for an index whose expression was LastName+FirstName would be "S" through "Sz". Alpha would only access those "S" records.

In order to be more generic than a range, LQO takes a part of the expression of the filter (e.g Between(LastName,"S","Sz) and looks to see if there is an index that uses the field of that part of the expression at the beginning of the index expression. It then tries to internally set a range that restricts the range of records that need to be accessed, and then applies the filter to only those records. So if your filter expression was

(FirstName="John") .and. Between(LastName,"S","Sz)

then ideally it would search the records only starting with "S".

Unfortunately, LQO isn't super intelligent. You need to help it a bit to find a matching index. It is best that the beginning part of the filter expression that you want to match to an index rearrange the filter terms to either start with a matching equality (or >= or <=) statement or use a Between() with constants for the 2nd and 3rd arguments, as in

Between(LastName,"S","Sz) .and. (FirstName="John")


LastName="Smith" .and. FirstName="John"

So for a last name search, Alpha Five would be just as happy with index expressions of


It may not work with things like


LQO may also not work with query expressions that use expressions that are complicated inside a between() like


But it may work with


Different versions of Alpha Five may be better than others in using LQO for more complicated expressions, but in generally, make sure it happens by keeping the expressions simple.

LQO will not help (and could slow it down a bit) if the range of selected records is a large percentage of total records. This is not typical, but happens if you create filter and index expressions incorrectly or reference a large portion of the table.

When you get the LQO right, your speed of accessing records for large DBF tables will increase a lot. That's the only way to really tell if LQO is happening (unfortunately it would be nice if Alpha had an LQO evaluator which would show you when it would actually happen)
Record Locking 101 (Email Comments) Updated 13 Sept 2011
(and how to speed up database usage including Forms/Reports/Appends/Updates)

In databases that are not "True" client-server databases, which includes Alpha Five's DBF file format, as well as Filemaker Pro, and Microsoft's Access (typical usage) and many others, there is a process that happens with each access of data that is referred to as "Locking". Since each client computer is actually writing to a portion of a shared file someplace on a network, this needs to occur so that data is not used that is only partially changed.

Imagine that data was in the process of being written to the shared file and you read the data at a point when only part of the data had been written. This would give you a mix of the old and new data.

To prevent this, writing and reading of a record's data must be performed in "Atomic" operations (one that can not be interrupted during the process). This includes parent records and every child record that is linked.

Locks are performed by the operating system
.  Alpha sends network commands to lock and unlock files and records, but the operating system actually performs the operation at the server (where the file is located).  This means that different versions of the operating system may do this better than other versions. It is also possible that default operating system settings may be different, e.g. Windows XP vs Windows 7, which means that you might get better performance on one over the other.  There may be settings you can adjust (typically registry settings), but may not be documented. If you are having a speed issue in Alpha associated with networking & locks, see the tip under speed here dealing with opportunistic lock settings.

Each lock has an associated time that it takes, and that time can stack up when you consider how many simultaneous file and record lock requests are being made by users. Since it is atomic, every lock request by other users and processes must wait while this happens until it's turn comes up. If using referential integrity, Alpha enforces write locks on the children records as well (referential integrity does not actually require this child locking, but is the way that it is implemented in Alpha Five). This atomic operation generally goes something like this;
  1. Request and wait for a "file lock" on all relevant data files of the database (typically, data, indexes, memo). Some allow you to cancel this step if waiting
  2. After receiving a file lock, request a "record lock" for each of those files for the needed records. There can be read record locks and write record locks for the respective needs, or they may be identical depending on the operating system. Reads are typically faster than write locks, as the writes have to read the original sector(s) on the server hard drive, modify the data on the record part, and then write back the sectors.
  3. Release the file lock while keeping the record lock.
The record data can now be used or changed. When done working with the specific records, the reverse process must be done to release the locks. For a read, the data is generally read quickly and released.

For an edit of a record (and its children records for some databases), it depends on the database program.

Some release it after a quick read, then later repeat the process to write the final changed data. If more than 1 process is editing the record, and tries to write the same record, the last values written generally is the one that remains. This is referred to as optimistic locking.

Others, such as Alpha Five, hold the write lock on the record forever until the changed data is saved, which means no other process can be editing the same record. This is referred to as Pessimistic locking. Should the computer that has the record lock have a program crash, the lock still remains, meaning no other computer system can edit that record. In some systems, this lock will timeout, in others it will not. Alpha Five does not have a timeout, but the operating system might. If it does not release the lock, rebooting the computer and opening that database on the computer that crashed will clear the lock.

The release process goes something like this;
  1. Request and wait for a file lock on all relevant data files of the database (typically, data, indexes, memo). Some allow you to cancel this step if waiting
  2. After receiving a file lock, release the record locks. 
  3. Release the file locks
These atomic locks and releases are sent by the operating system to the computer where the data is stored. It is fastest if it's the local drive (microseconds), a bit longer on a Local Area Network (LAN), and can be seconds over a Wide Area Network (WAN) (such as the Internet).

Usage on a WAN will slow a database to being unusable. However, there is a solution. Using a remote program like PCAnywhere, VNC, or a Citrix server, they move the programs operation to the LAN or local computer, and essentially just send an image and allow control of the program remotely over the Internet.

In some cases, the database program may actually hold the file locks for multiple accesses of record data, say 50 at a time. While the file lock is asserted, anything can be done. Then releasing the file lock so other processes can use it. This might be done for generating a report, or appending data etc. to lessen the impact of many record accesses, as the lock process has a fairly high overhead compared to reading and writing records. Some database programs may allow you to explicitly control this to "Batch" process data, however, Alpha Five does not. In Alpha Five, you can open tables with exclusive use if no one else is using it, but this would cause other processes to fail that tried to share the data.

In SQL and other True client server databases, the same lock processes basically happens, except now 1 computer controls the entire database. No file locks or file unlocks are needed, and the computer manages what amounts to the equivalent of a record lock. Any requests to read and write data go to the server, who honors the request to the data and sends back the data read, or updates the database with the new data. This gets rid of the time required to get and release locks over a network, speeding access to the data. However this comes at the cost of requiring more work for the server, that was previously being done at each computer. The server can allow multiple workstations to open the same record for change, and when the data is presented back, can manage which of the workstations' data will be accepted for changing, and/or flag differences between the multiple data being written, and manage those differences.

There is one other advantage of a True client server database. In a non-True client server database if a workstation crashes during the lock/unlock process, or, if on a WAN just happens to have a loss of communication, it can stop every computer using the database.

If it crashes while a record write lock is present, no one can change that record.

In most cases, these locks are released after the computer is rebooted, and in some systems (but not Alpha Five) may have some automatic time-out. But in a True client server database, the client workstation crashing just loses the data it was working with, and the server continues normal operation.

Alpha Five specific locking info tips
Disclaimer: Many of the items listed below could change between versions (or even patched updates) of Alpha Five. They are guidelines and should be verified for your usage.

Normally, for most normal record accesses in a shared environment, A5 locks (arbitrate might be a better word here) records (and indexes) on a record-by-record basis. If a record change causes a change to a record, or when a new record is entered, A5 performs a very short indivisible reading/writing of the tables and indexes that for that short time (typically milliseconds on a LAN), you effectively have exclusive control of the database "files" (tables/indexes), but not of the individual records locked by other users/processes changing another record.

If the table is being used by another process/user, (in shared mode of the table) you can perform all the normal single record type XBasic commands (except writes to records currently locked for writing).

Same thing is true for the more global operations like append, copy, crosstab, export, intersect, join, post, subtract, summarize and update. The most interesting cases most would use are the append and update. In these, the table's indexes are potentially modified.

Shared vs Sole Use during Appends and Updates
A5 has 2 different modes of operation for this kind of case. If the table is in use by another, it will update the index as each record is modified. No problem sharing here.

The 2nd way, if it determines it has sole use (still could be in share mode though), will do the operation and then rebuild the indexes. This temporarily locks (for what could be a long time, mind you) the tables from all other users. They will get a locking error with an automatic retry. As soon as A5 finishes the re-index (and it only rebuilds indexes that use fields that have been modified in the append or update), the file locks are released and the other users go on their merry (although a little miffed for someone locking them out for potentially a long time!) way.

Why do it this way you say? If the majority of records are going to be modified, it is much faster to update the indexes at the end of the operation. This can backfire when the number of changed records is small compared to the total size of the table. Assuming you are the sole user, where the break even point is, I've never actually tested, but I suspect it's something about where you change about 40% of the records.

To force Appending/Updating to use the shared method, you can not count on other users also using the table. To force this, open the table in a different session. It is easiest to just open a form (which can be hidden) while doing the operation, and close the form afterwards.

Some code that does it


Tests on a 35000 record file had an update take about 11 seconds in the above method, versus 19 seconds for the index rebuilds happening at the end (index expressions are very complex for this table) when the table is available exclusively.

Is the overhead of an Update operation slower than parsing through the records?
For large numbers of records where either the index rebuilds don't take long, or using the method shown above to update indexes on the fly, normally the answer is yes. For under 10 records, it is probably faster with code, with 10 to 100 records probably being the cross over point. It's even possible to use both methods, depending upon # of records to process, if the value changes dynamically.

Force LQO where possible

Depending upon your options selected in operations, the 1st thing it might do is perform a query of the source or destination tables. Make sure your filters will invoke LQO (Lightning Query Optimization), otherwise you will be doing a slow query if the query results include almost every record of the table. Each read of the record for a query requires record locks. Accessing less records (by using LQO) will reduce locking/unlocking requests on the network.

Open and Close of Tables
The Opening and closing of a table, either explicitly by code, or implied by the code being executed is a fairly long process over a network. (typically 25 milliseconds). For some code, e.g lookupc(), lookupn() and similar, they actually are much faster if the table is already open (at least in more recent versions of A5). This implies that they are using an already opened table, to save time. If you are performing a lot of these operations, it is best to open the table before the lookups, and close after all are done.

To check which functions might use an already opened table, try the code with a table open, and without, and measure the speed.

Of course, you should always close tables the code has opened on the way out of code for cleanliness (and more to not leave a file open in the operating system - you can't be sure Alpha will do it for you!)

If you have opened a file (file, table), Alpha Five will normally close it when the current code ends.  If you want it to stay open even after the code completes, use the table persist method

Report Locking
Alpha Five locks reports for the whole process while generating the report, unless you take specific steps to not have this happen. A report that either doesn't check the "Don't Lock Table" box on the report properties, or uses summary values that are not of the running type, or has widows and orphan turned on anywhere will lock the table, sometimes for a relatively long time. Having the "Don't Lock Tables" unchecked in the report properties (and this is the default), using summary values that are not running values, placing summary values at tops of groups as opposed to bottom, sorting/breaking groups differently from the sort of the entire report, using widows or orphans, using page x of y, are just a few things that slow the application down for everyone, by locking the table(s).

A report needs to be locked when it is required to make multiple passes through the table for calculations that summarize items. It guarantees that a pass 1 though the data to get some values needed to format the report is identical to pass 2 where it actually creates the report. Some of these double passes go all the way to the end, some just to the end of the current page or group.

E.g. to compute percentages, you need to know the total 1st. Let's say you don't lock the table. You pre-process the total of some number. Then as you process each record's value, you divide by that total times 100 to get the percentage. If some record that is part of the total is changed, or a new record is added, the actual total might change, but the total number that A5 computed will not change. It's possible you could have percentages totaling more or less than 100%

Instead, use calculated fields (on reports) that use functions like (which I'll call running summary values)
instead of

Following the above means that they must also be used in the footers, and can't be placed in headers, as it does not exist prior to the header being generated. Page x of y will also cause 2 passes as the y can't be determined until running through the data 1st generating a quasi-internal report.

Group Keep/Balance column height/Keep with detail/Balance column height (widows) Report Group Properties, Keep together on page (Orphans) Report Region Properties all force multiple passes and hence locking through the data.

As you might surmise, the running versions are much faster, and should always be used unless you must have the summary prior to when all the records are processed for the printing pass..

Simplify Sets
Use of a set that is overly complex for forms or reports is another bad idea, as it requires unneeded locks. Create a set tailored for the usage by the reports. Turn off referential integrity for that set and set any children to read-only where possible. For Forms, complex sets also pay a high penalty for a set's children records, so don't add more child tables (particularly 1 to many) than are needed.

Sometimes it makes sense to copy the needed table records to a local drive (could also be any place, but the local drive is fastest normally) and run a report there (this requires a fair amount of code). The copy goes very fast, and then only the local user is impacted by the report (and does not cause any locking of the shared tables over the network)

The general rule is never lock tables that need to be shared, ANYTIME, ANYHOW!

Xbasic versus Action Script Coding (Email Comments) Updated 21 Mar 2010

One should be familiar with the strengths & weaknesses of any tool they use, and XBasic or Action Script coding in Alpha Five is no different. Below should give you some of the advantages and disadvantages of Action Scripting and how to "bullet-proof" your code a bit more.

What is an Action Script?
Action Script coding contains individual Action Items. Each Action Item allows specifying in a menu or dialog driven sequence, a number of values and settings that generate an underlying XBasic code.

The Action item's underlying XBasic code is regenerated each time you save the Action Script.

Action Script Advantages
Action Script Disadvantages
Real-Life Action Script example vs XBasic
This is the XBasic code that an action script line to set a form field to an expression value generates:

'Set 'Value' property of 'TimeStamp' in Form 'TestForm' .
DIM form_name as c
if is_object(topparent.this) then
    form_name = topparent.name()+".this"
    form_name = ""
end if

DELETE expression_result
expression_result = eval("now()",form_name)
parentform:TimeStamp.value = expression_result

This is the same exact functional XBasic code without the Action Script un-needed code


In the Action Script, the actual code has the disadvantages of being slower, more complex, deleting a variable named "expression_result", that could have potentially been a global and/or shared variable prior to that point that would now not exist. If both global and shared levels existed before the delete, now the global will still exist. The "expression_result = " line will now assign a new value to the global variable "expression_result" since it was not explicitly dimensioned at a lower level. Either the global variable will change it's value, or possibly cause an execution error as the object type (type T for time here) does not match the current type of the global variable.

Most users of Alpha Five probably use Action Scripting more than XBasic coding. It is useful, particularly to non-programmers, but even as a 1st step for programmers. However, I would encourage moving to XBasic coding as your personal abilities allow.

Use Action Scripting as a tool to learn Xbasic. Use them to show the way or at least to point in a direction to go. Then convert the Action Script to XBasic code, take out the extraneous coding and redundant sequences and you almost have "normal" code. The difference will be faster, more reliable code, over the long haul.

In the long-term, the more Xbasic you know, the better off you will be. It will allow you to use the debugger more effectively and generate faster, cleaner code. Inevitably you will end up needing something Action Scripts can not address. Action Scripts can do many things, but it doesn't do everything that XBasic coding can.

Some more comments of Action Script Code Generation
The moment you save an action script being edited (even with no modifications), the action script dialog settings are evaluated and new code is generated. So let's say you created in release "X", but are now editing in release "Y". Saving the Action Script will generate the code per the new releases' Action Script generating code. So even if you did not change anything, a small edit of an Action Script line followed by a save, can result in potentially different (and untested) code!

A similar thing could occur in the way a function works in an Xbasic piece of code operates, but at least with XBasic code, your code would not have changed! Also, it is much less likely that a function would change in a bad way in what it does, versus the code that Action Scripting might generate.

So, one could argue that the prudent thing to do would either change the code to XBasic so that it will not change, or make sure the underlying Xbasic code you start with, is the code you end with (except for your changes) when you edit in a new release.

Despite the surface appearance that they are the same, they are actually radically different. A function (Alpha's or a user's UDF has code that is syntactically compiled without errors otherwise it would never be able to be used) is tested for what it is.

But Action Scripting can generate myriads of combinations for the settings of a single action script line that would generate code that was never validated. In probably 99.9% of the cases this is fine and works as expected. But not always (see this thread Re: Variable Type "H" )

Some Action Script lines are very simple and straightforward. These seldom have issues, as there is almost a 1 to 1 correspondence to the XBasic code they generate. Others actions generate code from many selections of menus and settings. These generate a string of code that needs to be compiled (which Alpha does when you save it) without errors, and more importantly makes sense for all the settings. While, in general, each setting (or multiple settings) of an action script builds a portion of the code, there is no way every combination of possible codes could ever be fully tested. Some parts can be very straightforward to generate (and to test - this is why it generally works) and other parts depend on multiple settings which can be a nightmare to generate and can create extremely complex code.

In most cases, there is a very specific code generation sequence where you take setting "A", generate it's code, take setting "B" append it's generated code and so forth. But then there are cases where "B" implies what "A" generates, and where it needs to be located in the code, and so forth. It can get so intertwined, that it's impossible to totally predict what it will or could generate and these are where the typical failures tend to be.

AEX Compiled Librarys (Email Comments) Updated 18 Mar 2013
The CSDA Code Utility has an AEX library compiling utility called LibMake that simplifies the process of making a desired library. Whether you make an AEX library with LibMake or Alpha Five's built in tools, there are items you should know when using AEX's.

What is an AEX?
The AEX is a "Compiled" library of your code, toolbars, menus, images, blobs and other data from your database application that comes from the global items stored in the database.  All of these come from the database files ending in ALB, ALX, and ALM. The created file ends in the AEX extension. The library is useful for protecting your code from examination and modification, unless you have the original code. This helps protect your intellectual property. The debugger will not operate on AEX items, eliminating that as an avenue for looking at code.

AEX "Compiled" type
Alpha Five's "compiled" AEX format is not a truly compiled code, but what is generally referred to as pseudo-compiled.  Portions of the code have been translated into internal commands, but essentially most of it is still a program's internal representation of your code text, and still must be processed. when executed.  For all intents, there is no measurable speed improvements when executing code by using an AEX compiled file. Alpha Software has experimented with a real compiled code version, and it runs much faster than normal Alpha Five code, but there are many aspects of conversion that don't work or are not compatible, so it may never be available as a product.

AEX loading speed
When an AEX file is loaded, that file needs to be read by Alpha Five.  The size of the file, typically is relatively small.  The largest one ever created is probably about 57 Megabytes and about 7000 entries from Alpha Software (I'm guessing #2 is the CSDA Code Utility AEX at 3 Megabytes and 13 entries - 500 internal functions don't show). In most cases, it's size is typically very small, and would not have a particularly large impact to a fast network.  The one case where it might have a temporary impact is in a large network (100's of workstations maybe), who all start Alpha Five at approximately the same time.  A similar startup network impact event would occur if you used Network Optimize (shadow) and had to refresh the shadow the copies, all at the same time.

For easier maintenance, it might be be better to leave the developer created AEX files on the network in the database's main folder, and load them by Xbasic code, for most usages.

AEX protection of Intellectual Property
An AEX essentially translates your code to an Alpha Five's "compiled" internal format. If you use a file "hex" dump utility, you can normally see most of the expressions and operands of your code used in your code as ASCII text. However, if you use the code line


all of the code after that point in your code will have the ASCII encrypted so that it is not readable in the AEX.  If you wish to have portions of the AEX visible, use


Things that should be in plain text include Copyrights, Help files and similar that you want users (and people in a court of law) to be able to see.

If you use AEX files to distribute your code, and you use the commands above, there is no reason you need to password protect your original code, making it easier for development

addin_library.AEX file
All of the AEX files from the Addins_Installed folder(s), are combined into one large addin_library.AEX file at startup of Alpha Five. This combined AEX library is available to any database you have open with Alpha Five (which may not be what you intend). Any AEX libraries that have an item in it with the same name and same type, will result in only one being in the combined AEX. If they were not identical, this could execute the wrong copy of the code.

In general, the addin_library.aex file is rebuilt each time you start Alpha Five, if it doesn't exist or if you have made any changes to the files in the Addins_Installed folder(s).  After that rebuilding of the addins_library.aex file, the Addins_Installed folder(s) is not used, so you can change the individual aex files there as needed to update.  If two copies of the same version of Alpha Five are open, you will not be able to rebuild the file (which is OK, if you have not made changes to the Addins_Installed folder(s).

Loading of Other AEX files
If an AEX file has the same name as the current database and is located in the database file's (the same folder as the one with extension ADB) folder, it will be automatically loaded when you start that database.

Other AEX files that you specify may be loaded automatically from various settings in Alpha Five.  You can also manually (meaning XBasic code) load and unload AEX files.

AEX Addins_Installed folder issues

The Addins_installed folder is normally where you place AEX files, that will automatically be loaded when you start Alpha Five.

In A5v7 and earlier the addins_installed folder would be located under the Alpha Five exe folder.  Starting in A5v8, an additional folder was added in the user's application data folder area, for use in Windows Vista and later. The AEX files could be located in either location and work properly. Early patches of A5v8 did not totally work, but later patches did it correctly. Then in A5v9, the code was changed in an early patch, and then no other Addins_Installed folder other than the one in the Alpha Five exe folder will actually work. The Addin Manager Tool may show the scripts and functions from the other AEX's in other Addins_Installed folder, but they are not actually loaded. A5v9 and up (at least to A5v11) currently DO NOT WORK FOR THESE OTHER FOLDERS!

The install of CSDA utilities like the CSDA DiagInfo Utility actually test all the folders during the install process, and will place the CSDA AEX file only in the best folder that actually works, so you can test new releases and find out where an AEX actually works. It will also show you AEX files info that may prove useful for debugging.

Also, Alpha Five rebuilds the addin_library.AEX file anytime an AEX file that makes it up changes, but the code doesn't always catch the changes depending upon the version and patch release.  If you delete the addin_library.AEX file, it will be rebuilt the next time Alpha Five is started.

AEX item name conflicts
If more than one item has the identical name and type is loaded from more than one AEX file, only one will be used (probably the last one loaded).

If you have an entry in the database with the original code, that will override all loaded AEX entries by the same name and type
.  So, to make the AEX be used, you must remove all entries from your database that you want to use from the AEX.  In general, this means having one operational database copy with all the code deleted from it (and hence uses the AEX version's copy of the code), and another development copy with all of the original code in it.

AEX Library number
Depending on whether the AEX libraries are loaded from Addins_Installed folder(s), or individually, the library numbers there were created with may or may not be important. The final library numbers you use should not conflict with others being used. The CSDA DiagInfo Utility will show which AEX files and numbers are currently in use.  You should avoid using any Library numbers show below as well.

Alpha Five currently uses the following library number assignments:
Library # Purpose
0 Alpha5.AEX, Startup.AEX
1 System.AEX
2 modern.AEX
3 classic.AEX
100 Temporary for addin_manager explorer, and default compiled number
125 used in loading aex files
126 used for aex with database name
127 used for Addin_Library.Aex (which combines all installed library AEX's and ignores their library number)
200 used by web server to load AEX libraries to a web page
60-63 for AIMS products
64 for CSDA Code Utility and for AlphaSports.AEX

Coding tips (Email Comments) Updated 07 November 2011
Network Issues related to speed (Email Comments) Updated 16 April 2012
The speed of Alpha Five can be effected by various network settings and how the network is used.
  1. You have a problem with networking and Alpha Five
  2. You know what you are doing
  3. You've tried everything else

Speeding up code (Email Comments) Updated 12 November 2011

For the most part, the speed of XBasic code does not change from one Alpha Five version to the next. There are a few exceptions to this, typically when Alpha Software totally rewrites a function, or changes it to a "compiled" (not an AEX-type compiled) type code. The following are tips that can speed up an application.
Isolate Code that depends on tricks (Email Comments) Updated 03 Jan 2010
Any code that uses special tricks or depends on an odd or undocumented (or less documented - meaning hard to find documentation) way that Alpha operates, or is Alpha Five version-specific, should first tried to be avoided. These could lead to future support issues with the code.

However, if you still think you need to use this, place the code inside a function. It should contain a lot of comments in the code documenting the trick or usage so that anyone (including you in the future) understands what is happening.

By placing it inside a function, you can update it easily as needed in the future to account for slight variations between Alpha Five versions or patches.

An example is the code ""+numbervalue

This code converts the numbervalue to a string, keeping all decimals, but without trailing decimal zeros and with no leading spaces padding. There are other ways to do it, but they can be more complicated. Since the details of the conversion is not documented (in this case keeping all decimals), it might change (although unlikely) in a future version.

Placing code like this in a function will allow replacement with a working version should it prove necessary.

Here is a version specific example that returns the current EXE name, should it be renamed. It is the typical way many might write version specific code.

FUNCTION get_exe_name AS C ( )

IF version()>=8


The above works fine, however, there are times when specific builds (patches) of Alpha work or don't work. You could process each case as needed, but this can get to be very hard to check each build. In this case, and in any cases where you can test the operation of the "trick", you can dynamically test it and use some alternative value or method when it doesn't work. For the above, rewrite it this way, and now it is not version-specific, but whether the method works or not-specific.

FUNCTION get_exe_name AS C ( )

IF eval_valid("a5.get_exe_name()")


If the test requires a lot of time to process, continue storing the final result or whether the trick works so that you need to only do the test once

Protecting Code (Email Comments) Updated 09 Jan 2012
Protecting your code is important if you want to sell a product. There are a variety of ways to protect your code in Alpha Five.

The protection of code on layouts such as forms, browses and reports is based on protecting the layout with a password. These passwords are relatively easy to break, so I recommend that you not depend on them. If you do use them, do not use the same password in code objects within an Alpha Five application.

Operations (e.g. Update, Append etc) also have the same password limitations as layouts, so you are best just converting the operation to an XBasic equivalent, and then using code techniques described below to protect the code.

To protect code on layouts, it is better to move the code to Global Scripts and Functions (although functions should be used - see here). Then your code in the layout amounts to a single line of code call to the global script or function. This moves the need for protection to the global script or function.

In a similar way, layout calculated fields can utilize global functions to hide most of what they are doing, although there is a small speed penalty in the overhead of executing the function (which can be a bit higher, as layout calculated fields may be executed several times to get the final value, rather than once)

A global script or function can be protected by having the 1st line of code that looks similar to:

'password your_password

The code debugger will not step through password protected code, so that helps.

However, many development projects require exporting the code and will require re-entry of the password for each and every code object being exported, even if previously entered. Also, a global search of code ignores any code with a password, so passwords get in the way of maintenance of code.

The CSDA Code Utility has replacements for both of these that are more complete and allow for entering a list of passwords to use for both of these operations.

Action scripts can actually have their 1st line modified to include the password line, but each time it is edited, the password line is removed. There is the AIMS Grab Bag tool from AIMS that will add the password to each Action Script after being edited. Alternatively, just convert those Action Scripts (recommended here) you want to protect to an XBasic script and password protect that code.

Another problem with password protection of code is that anyone with Alpha Five can compile it, whether or not it has a password. Unless you follow the technique for AEX files that follows, much of the text of the code will appear in the binary of an AEX file

In Alpha Five you can do a compile (actually a pseudo-compile) of code into a compiled library (extensions AEX). Like passwords in code, the code debugger will not step through compiled code.

Compiling works on password protected code, creates a small problem. Much of the text of the code (whether password protected or not), is left visible in the AEX file. A simple binary hex view or dump program can see these text strings. To make sure these strings are encrypted, and to always protect your code, use the following line:


All code that follows the above will not have readable text. To turn the text strings back on, use:


You can go freely between the 2 as many times as you wish in your source code.

In CSDA code, only those sections that we want visible are readable. Typically this is built-in help text, copyrights, and code date and version numbers.

Another aspect of global code is that all global functions are accessible, even if they would never be used by others, or if you don't want them used by others. If you make them an internal functions (any function code placed in the a global function or script's code with a name other than that of the global code), only that code can access the internal functions, and an external function of the same name is ignored (giving precedence to the internal function)

The LIBMAKE() function of the CSDA Code Utility creates AEX compiled code libraries. Using LIBMAKE(), you can keep all your internal functions as separate code to make it easier to maintain the code (especially when using it in multiple places). When you compile using LIBMAKE(), it will compile only the code you want into a specific library, and at the same time, include each desired function as an internal function to each code that requires it.

For example, if you look at the viewable functions (using the Alpha Five Addins Manager) of the CSDA Code Utility, there are only a few accessible functions, yet there are many internal functions that make each of them up, many of them repeated.

The only real limitation is that the any code object with all included internal functions not hit certain physical limits that Alpha has for code sizes (as discussed here)

When using variables, it is best to make them local when possible (see here), which means that they don't exist after the code closes. Making variables Global or Shared may create an avenue to which others may be able to affect your application's operation.

There are additional ways to protect your code from prying eyes, but you have the basics listed above.

Using Active-X Objects tips (Email Comments) Updated 27 Feb 2010
Please note the following:

1. Xbasis code using activeX controls can only reference the Active X properties (variables) with one pointer level (one dot). This is due to an issue with the Alpha Five XBasic Interpreter and Visual Basic Interface. To solve it, set the parent pointers of a variable to a temporary variable pointer, and then use that pointer to reference. E.g.


should be replaced with code similar to this

dim temp_pointer as p

2. Active X Arrays referenced from Alpha Five start at base of 1 for the 1st array element vs the Active X control bases it at a base of 0. Hence add 1 to the array element when using in Alpha Five Xbasic. Also, reference to arrays in XBasic are always addressed with [ ] square brackets. E.g. the lines in visual basic

objectpointer.arrayvalue(9)="test string"

should be replaced with Xbasic code similar to this (taking in to account both issues)

objectpointer.arrayvalue[10]="test string"

Bitwise Logical Operations (Binary) with numbers (Email Comments) Updated 14 June 2012

When interfacing to other programs using Active X controls or similar, they often use numeric values to act like having many logical flags, one per binary bit. Each binary bit (base 2 arithmetic) can have a value of 1 or 0.

However Alpha Five only operates with decimal numbers in it's expressions. So any time you have values in other formats, typically either binary (base 2), octal (base 8) or hexadecimal (sometimes called just hex - base 16), you must convert them to decimal number values

So if you had a hexadecimal number 23, typically shown in other languages and programs as &H23 or 0x23, this is equivalent to the following

Value Base Valid digits for base
23 hexadecimal 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
00100011 binary 0,1
043 octal 0,1,2,3,4,5,6,7
35 decimal 0,1,2,3,4,5,6,7,8,9

To convert a hex number from some other language to decimal in Alpha, Alpha needs for you to give the hex number portion as a string, so for the above, you might use

numeric_var=hex_to_dec("23") ' Translate Hex string &H23 to decimal number

Alpha Five does not have any binary or octal functions to convert those formats.

Once a numeric value, Alpha Five logical operators,
will all work on each bit of the binary equivalent as if you performed the logical operation on each bit position of each side. So if doing a bitwise .OR. , an example might be

Expression Result Base Valid digits for base
23 .OR. A1 A3 hexadecimal 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
00100011 .OR. 10100001 10100011 binary 0,1
043 .OR. 121 243 octal 0,1,2,3,4,5,6,7
35 .OR. 161 163 decimal 0,1,2,3,4,5,6,7,8,9

Since Alpha Five's expression parser understands only decimal numbers, you must convert them all to decimal numbers if not already in that format, e.g.
hex_to_dec("23") .OR. hex_to_dec("A1")
35 .OR. 161

Later if you need to change it back to hex, use the function dec_to_hex().

Inverting the Bits (Bitwise NOT)
As it turns out, to invert the bits (change 1's to 0's and 0's to 1's), one might expect the .NOT. operator to work on the bits, but it does NOT work. The easiest way to invert the bits is to use .XOR. with the value on 1 side and a decimal equivalent of all binary 1's (for the needed width) for the other side. So to invert hexadecimal 23, for a width of 16 bits, we would use an expression like
hex_to_dec("23") .XOR. ((2^16)-1)
which simplifies to
35 .XOR. 65535
and yields

Expression Result Base Valid digits for base
hexadecimal 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
00100011 .XOR. 1111111111111111 1111111111011100 binary 0,1
043 .XOR. 121 377,334
octal 0,1,2,3,4,5,6,7
35 .XOR. 65535 65500 decimal 0,1,2,3,4,5,6,7,8,9

Accessing specific Bits
To operate on a particular binary bit in "n" (where n is the bit position starting at 0 on the right most bit) in a long word of binary width "w" (typically values, 8, 16, 32, 64 which correspond to 1, 2, 4 or 8 bytes of information), the following expressions should be helpful

numeric_var=numeric_var .OR. (2^n) ' Set a bit at position n in a binary representation

numeric_var=numeric_var .XOR. (2^n) ' Invert bit at position n in a binary representation

numeric_var=numeric_var .AND. ((2^w-1) .xor. 2^n) ' Clear bit at position n in a binary representation

numeric_var=numeric_var*2 'Shift bits left 1 position, and zero-fill lowest bit

Test Bit n for a 1
IF((numeric_var .AND. (2^n))>0,.T.,.F.) ' True if bit n is set to a 1, else false

These are not the most efficient ways to do these, as in most cases the numbers n and w are known at the time of writing the expression, e.g. 2^2 is just the constant 4. And likewise
((2^8-1) .xor. 2^2)
is equal to
255 .xor. 4
which is equal to
In addition, you can do multiple bits simultaneously by combining individual bit expressions together

Bit operations only work for lower 32 bits

The logical operators .AND., .OR. and .XOR. only return the lowest 32 bits of the result.  Any bits in higher bit positions are effectively set to 0 in Alpha Five Version 8, however in Alpha Five version 11, any bits past the lower 32 bits seem to result in the top bot of the 32 lower bits being set. Below is an Interactive Window Alpha Five version 8 example.
= 8589934592
= 1073741824
= 9663676416
?((2^33)+(2^30)) .OR. 0
= 1073741824

 Also, when the logical operation results in a value that has the 32nd bit position set (the 31st position if starting from 0), the result will return a negative.  This can be easily resolved by taking the absolute of the result.  Below is an interactive Window example.
=> 2147483648 (decimal)
?(2^31) .OR. 0
= -2147483648 (decimal)
?abs((2^31) .OR. 0)
= 2147483648 (decimal)

Blob bitwise operations
Using Blob functions, there are blob operations OR, AND, XOR and NOT that will work on each byte of the blob in a bitwise manner as well.

Alpha Five Hardware Suggestions (Email Comments) Updated 15 August 2011

Alpha Five utilizes hardware in ways that by careful choice of the hardware, can have it's speed and reliability increased. Below are some hardware concepts that apply to Alpha Five's usage and guidelines for hardware selection. As technology changes, the recommendations are generally for systems that have a good price/performance ratio.

Desktop Clients
A Desktop Client computer runs Windows and the Alpha Five program, accesses the Application layouts and code from either the LAN network or, if Network Optimized (shadowed), a local drive. Most data is placed on the Desktop Server so that others can share and process records as well. Speed of a Desktop client does not have any effect on speed of any other client, with 1, maybe 2, exceptions. Many people use 1 of their Desktop Clients serving the dual purpose as a Desktop Server, which can cause other desktop Clients to be much slower if that server is also being used as a client. Also if the Desktop client is slow in locking and unlocking on the network files and records, they will slow the other clients down while performing these.

Each and every client system needs to be reliable. In fact they need to be as reliable as a Web Server! If they have issues with their memory or other processing data issues, they can quickly corrupt indexes and data. And it only takes one bad machine on a network to corrupt a shared database. Most of the times, a machine that might do this is likely to crash before causing a corruption, but you never know. If you have any kind of irregular corruption issues of data or indexes, it could be one of your client systems or the network connection to them. Good Backup procedures and regular re-indexing (and checking indexes for errors) is a good procedure to do.

First and foremost, the main component for speed is the networks. For the desktop, any problem with the router, network switch, cable, connectors and NIC (Network Interface Card) can slow the system down, a little, or a lot for all users!

Using wireless should always be a last resort choice, as a 1 GB Ethernet runs much faster and is very cost effective today. Depending upon your electrical power in your building, PowerLine adapters (basically they extend your Ethernet LAN through the AC power in the building, with speeds up to 500 Mbs - depending on the adapters, although slower speeds are more typical in many buildings) are often a good choice, and better than wireless.

The network switches (or routers) and NIC should also be at least 1 GB Ethernet. There are some switches and NIC cards that might be more efficient, but for the most part, they all operate reasonably well in today's market. However, 1 bad NIC, or Ethernet cable, or connector or router/switch can slow the entire network traffic down to a crawl. This happens because network packets are sent with errors in them, and have to be re-sent, possibly many many times, until it is successfully received.

Next, the speed of the hard drive is important. You could use a RAID, but that might be overkill for most needs. However, it does make sense to place your application on a different physical drive from Windows. This makes head seeks on the hard drive independent for Windows and for your application. A SSD (solid state drive) is good for files that don't change a lot, mostly Windows code and executables. Using an SSD is even a good place for a shadow (network optimized) Alpha Five application.

Currently, only one CPU core (Single threaded) is used by Alpha Five, but it may be possible to have Windows operate with the other cores. A reasonably fast CPU is important, but not necessarily critical. It really depends how much XBasic code is running as to whether it will make a big difference, but in general, get a fast CPU, 2 or more cores, but don't pay a premium price for it.

Memory is important for Windows operating and efficiently, and Alpha Five can use it too. I suggest that 2 GB of main memory minimum based on current memory prices, 1 GB if you are on a very tight budget. If possible shoot for 3GB or more. 32 bit versions of Windows have a usable limit of 3 GB, even if you put more in.

Desktop Server
A desktop file server handles locking of a database requests and all the actual sector read/writes. It does not process any Alpha Five code. Instead it handles file read/write requests sent from Client systems, as well as processing all lock/unlock requests (which are the critical speed item in a non-true client/server application (e.g. DBF files). Speed of it's NIC card and how fast it can process multiple file read/write and locking requests is important.

CPU speed is less important than the client, but Hard Drive speed and 2 Gb or 3 GB minimum memory for caching data is important. This system should be very reliable though and have a UPS (Un-interruptible Power Supply) on it.

WAS (Web Application Server)
If using the Web Application Server (WAS), the Internet interface speed will be your gating factor. Since the WAS computer has to do everything, make the CPU as fast as your money allows, but consider a reasonable price/performance point.

Fast and larger memory (3 GB is probably plenty for most) & fairly fast hard drives will minimize latency contributions from those. Try to put the actual data on a different physical drive. If running an SQL database and money allows, place the SQL server on a different computer (or assign it to other CPU cores that are not being used), to allow the fastest processing of SQL requests.

Video card and other devices have no bearing on speed.

This system should be very reliable though, allowing 24x7 operation and have a UPS (Un-interruptible Power Supply) on it.

Alpha Five Data & Index Corruptions (Email Comments) Updated 26 August 2011

In a DBF file structure, which Alpha Five utilizes for DBF Files and Data Dictionary files, Index corruptions are found sometimes and less frequently, data corruptions.

The 1st thing people blame is the software. It is almost never the case. There are things you can do in software to affect the efficiency of data (any data - data, indexes, memo fields etc) reads and writes to the server computer, and this will decrease the likelihood of a corruption, but it is not addressing the source of the problem.

It is almost everytime, the hardware associated with a client computer that is the cause. Because of the way files and records get locked, and the fact that the client computer does most of the work (except the reading/writing of disk drive sector data to the database file, any corruption of data by the client computer is generally due to it's memory or network usage.

Questionable memory or bad memory timing and memory setup is one typical cause. If the computer is crashing windows on a regular basis, memory or computer bus timing is a key suspect.

The network path (Network Interface Card (NIC), cables, connectors, swiches etc) till the common point of the server can also be a problem. An intermittent Network wire, or one that does not have a solid connection (even if it tests good for continuity), can cause electrical noise in the cable. While networks have error checking in them, occasionally one bad packet of data that is interpreted as good will cause a corruption. Maybe it's 1 out of a billion, but it doesn't take long to do a billion packets of data. Once an index is corrupted, it will generally go from bad to worse over time. Rebuilding indexes can solve an immediate index corruption, but does not remove the source of the problem.

Multiply the risk by the fact that it takes only 1 bad client computer sharing a database to bring down the entire database by corrupting data or indexes.

Despite all this, most systems tend to run extremely reliable.

What can you do?
First and foremost, backup regularly, typically once per day. Keep older backups on a progressively spacing. E.g. 1 backup for each day of the week, 1 backup for each week of the month, 1 backup for each month of the year, and 1 for each year. Except for the yearly backup, just reuse the oldest for the group, e.g. use monday's backup and replace the older Monday backup. This process allows you to go back to older data before something is corrupted, even if you didn't detect the corruption for a long time. All backups should have error checking built-in to the backup files. ZIP files and other compressed files all do this. You want to be sure of the validity of the backup to being faithful to the original. For most smaller businesses, a set of flash pen sticks can serve the backup needs and allow rotation, and even be easily taken off site in case of fire or other disaster.

To find the bad hardware, try to eliminate questionable client computers from accessing the database and see if the problem goes away. Or replace the client computer, or anything in it's network path with a new computer or network path items (wire, connectors, NIC card, switches)

See if there is any common part to all suspected computers (e.g. a network switch). If so, replace that item.

Sometimes network sniffers (hardware or software) can identify bad network packets. See if you can trace to a client computer that generates a lot of errors,

Use Alpha's built-in index checker to see if any indexes are corrupted on a regular basis and see how long it is between corruptions. This will give you a frequency of failure, and maybe give a clue to the computer by recent users (particularly big users)

Rebuild indexes regularly, to eliminate problems in the indexes.

Finally, you can switch your data to an SQL or True Client Server database of sometype. While this can be somewhat painful for conversion, it eliminates the client computer as a source of corruption, and places the entire responsibility for data integrity on the server computer.

Compatible DBF/Index Naming usage (Email Comments) Updated 09 November 2011

If you are using a native Alpha Five Database (*.dbf files), Alpha Five allows longer field names and longer index names (up to 24 characters) than the standard 10 characters that the format uses. These extensions are held in the dbf files' associated data dictionaries, which is an Alpha Five enhancement. Should you try to access the data from another dbf compatible program, these extensions are not available to those programs. They will get only the 1st 10 characters (possibly modified if not unique).

To allow easier usage by these compatible programs, and so you don't lose connections (e.g. in sets) due to losing data dictionaries, keep index names and field names to 10 characters or less.

Many places in Alpha Five can not deal with spaces in Database Names, Field Names or Index Names. It will sometimes convert the spaces to Underscores ( _ ), but it depends upon where and how it is used, making it inconsistent from a program perspective. Instead, don't uses spaces, and use the underscore instead. So acceptable characters for database names, field names and index names are A through Z, 0 through 9 and underscore.

In Summarize operations, the field names in the summary database seem to allow up to 32 characters in version 9 and earlier, and more in version 11 and up.  Version 10 values has not been tested.  It may be that they have extended the fields in recent versions of Alpha Five, however, the prudent thing to do is to use shorter field names in the database you are summarizing.

Alpha Five Path usage (Email Comments) Updated 25 April 2012

Alpha Five generally stores filename and paths as either just the filename and the path is assumed to be the current database path (either the Master or Shadow database path for whichever you are using), or the full paths and filename.  It does not store any relative paths, unless you create code to do it.

Occasionally, you may see some Alpha Five configurations and options that allow setting a path for particular files, e.g. an image path, but these are the exception.

Alpha Five also allows aliases used in a filename, where an alias name (surrounded by square brackets (e.g. [imagepath] ) used in a filepath is replaced by a piece of text that is stored in the database.  This allows other paths to be replaced, but beware, that not all file related functions do the alias replacement.

Using code, you can get the Master or Shadow database's path, then calculate another path that is relative to that.  You can also obtain Windows and Alpha Five folder paths that they use using functions built into Alpha Five, and base your path from those starting points.

An example of code to always get the Master database path (including the trailing backslash), even if you are running the shadowed version is

dim MasterPath as C

Variable & Code Naming warnings (Email Comments) Updated 04 February 2013

Variable & Code (e.g. script, function, classes and enumeration) names should be 24 characters or less. Part of the reason is that the ALB, ALM, ALX extension files where the code is stored only has room for 24 characters in the stored name.  So anything with the same initial 24 characters will be stored to the same place in the data dictionary.

In some older versions of Alpha Five, 23 characters is a safer choice, as some of them had issues with the 24th character. They can start with any character from A to Z or underscore ( _ ), and may use A to Z, 0 to 9 and underscore for the rest of it. Note that this 24 character limitation is for each part of a pointer variable (between dots), so the entire length could be much longer. So this is OK


If creating a pointer variable name, do not name it any of the following
When used in an expression, Alpha Five can't properly evaluate the result you intended. E.g.
dim glv.xor as p
dim glv.xor.char as c
ERROR: Unexpected text '.xor.'

ERROR: Argument is incorrect data type

True and false values of .T. and .F. don't cause problems, but should probably be avoided to avoid confusion.

Other variable names to stay away from are GLOBAL, SHARED, SYSTEM, PRIVATE,
BACKGROUND, PRIVATE, EVENT, PROTECTED, STATIC, CONSTANT. All of these will definitely cause problems in DIM statements, and as stated in other tips, every variable should be DIMed.

Code names should be 24 characters or less, with the same restrictions as variables. Because Alpha adds a digit at the end if using multiple copies or recursive usage (an alias in Alpha Five speak), it is better if the last character of the name is not a number, so it is not confused.

It is actually possible to create code names that are longer, and Alpha Five actually does this

In earlier versions of Alpha Five (Maybe version 9 and earlier, but maybe version 10 as well), if you use an internal function, do not have it's leading characters the same as the entire name of the code object. This causes all sort of odd problems for finding the internal function, depending upon if it was recently saved or not, etc. And debugging is an absolute nightmare when this happens. E.g if the name of the code object is MyFunction, do not have an internal function of the name MyFunction_Internal1

Regular Expression Notes for Alpha Five (Email Comments) Updated 11 September 2011

The Regular Expression engine of Alpha Five is a "Regex Directed" Engine. This can be proved by trying the following in the Interactive Window
?regex_grep("regex-directed not","(regex-directed|regex-directed not)","\1","i")
= "regex-directed"
If the results are "regex-directed not", then it is a text-directed engine, not considered as adaptable.

The whitespace "\s." matches a space, tab or or linefeed.  A Carriage return is ignored. This can be proved by trying the following in the Interactive Window.
?regex_grep("space "+crlf()+"2tab"+chr(9)+crlf()+"3cr"+chr(13)+"4lf"+chr(10)+"5line 6"+crlf(),"([[:alnum:]]+[\s]*)[[:alnum:]]{1,1}","~\1-","i")
= ~space
-~line -

The dot "." character matches any character.  A Carriage return is ignored.  This can be proved by trying the following in the Interactive Window.
?regex_grep("space "+crlf()+"2tab"+chr(9)+crlf()+"3cr"+chr(13)+"4lf"+chr(10)+"5line 6"+crlf(),"([[:alnum:]]{1,1}.*[[:alnum:]]{1,1})","~\1-","i")
= ~space
5line 6-

UI_Msg_Box() Codes (Email Comments) Updated 02 October 2011

Style            - Bits 0->3 values = 0 to 6
Display symbol    - Bits 4->6 values 0 to 4
Selected Button    - Bits 8->9 values 0 to 2

Good Alpha Built-in Functions to use (Email Comments) Updated 22 April 2012

Alpha Five Functions Converted to "C" code
Newer Alpha Five versions occasionally have functions rewritten from XBasic to a "C" compiled code. These generally are done to improve speed in Alpha Five.  Here is a list of functions, and the first version when they were converted to "C" compiled code.  While they should be 100% equivalent, when the functions are changed, there is a very small chance of a code bug being introduced.

Most Useful and Efficient Alpha Five Built-In Functions
Below is an edited list of the functions use the most by CSDA (ones that are specific to CSDA usages such as SIN(), COS(), date conversions and utility oriented are removed to give a better indicator of those that a typical user would utilize the most). Note that type C and M are used the most (and are faster in general), and type S (Xbasic functions) don't show up as much.  The counts give a relative idea of frequency of use. Note that the 1st XBasic ("S") function is only used 245 times by CSDA.

Any function starting with an asterisk (e.g. *count() ) is always a "C" type code

UI dialog functions (such as UI_MSG_BOX() ) that are type C, will stop timing events in XDialog message boxes from occuring until these UI dialog boxes are closed.  Thus it is recommended to replace them with a replacement function that uses XDialog if at all possible.

The functions below should be used as a guide to the best functions to use for speed and efficiency, and should be studied as a core group of functions to generally use.

In general, the ones near the top are faster than ones that are lower in the list, and should be used in time critical code, and are generally the fastest functions for their purposes in Alpha Five.  This is based on A5v8 - a5v11

Used: 78544 functions
Unique: 3027 unique functions
Counts | Types

Count,Type,Code Name,Prototype,Description

Data Security Tips (Email Comments) Updated 17 December 2011

First, don't store any personal data (e.g. Credit Card Numbers, Social Security Numbers, Birthdates, Maiden Names, etc) in regular text. If possible, pass the buck. Let someone else (e.g. PayPal, Amazon, Google etc.) store this information and take the risk if possible.

Second, IF you do store personal data, see if the First solution is not better. Otherwise, encrypt those fields. Someone without the decryption algorithm is immediately locked out of the data.

Then Any encryption/decryption keys used by the program should be hidden as best as possible within the program (generally this means encrypting tokens for an AEX, or a password on XBasic code).

Licensing your Application Tips (Email Comments) Updated 29 April 2012

The only way to stop unauthorized usage of a desktop application is to force it to work at only the current computer or location.  This also assumes also that a person can't look at the code or discover the methodology or algorithm you employ (see Protecting Code).  There are a number of methods that can be used.

The simplest way to eliminate business copying is to embed the name/address of the business owner right into a pop up message, telling people that it is licensed only for that name/address. This could be encoded into an encrypted serial #/activation code, so the code would not have to be changed. This relies on the idea that a person allowing others to use his license could more easily be caught, and does not always work. It also assumes the displayed name is valid, and not a made up name.

Other ways are tying it to a machine/location specific attribute. Some of these include the machine name, user name, network name, hard drive serial #, the MAC address (Network card ID), CPU serial #, Windows serial #, Alpha Five Serial #, etc, and in some cases, could be to a hardware key (dongle). These all have drawbacks whenever a user changes his system (approximately every 3 years), or updates to a new version of software (changed serial #).  However, a replacement computer can easily be set to the same machine name as the computer it replaced, and thus use the same licensing keys if based on the machine name.

Activation requires the user's computer send you some info about the system, and you generate an encrypted equivalent that you send back to the user that unlocks the software. This needs to be done via e-mail, web server, or similar, or by a phone for those who do not have internet. This is essentially how Alpha Five does it.

Once you have one or more machine-specific values, the program can generate an encrypted software key (normally should be standard, case-insensitive characters, such as hexadecimal characters) such that it would be difficult (within reason) for the user to ever generate it.  Then that software key (with machine specific info) is sent it back over the web, email, or phone.  Now you have to generate an activation key that is sent back to the user "activate" the program.

This activation code may have an expiration date encoded in it if it is a renewable or trial license.  If this is the case, you will also need to validate the actual date and compare it to the encoded one in your program.  One way is by checking on the internet.  The computer's date can be set to earlier values in order to bypass the expiration, so you need to make sure that the date is never earlier than the highest date ever seen. However, not all machines are connected to the internet all the time, so you may want to allow for that contingency.

Whatever the activation code is, create an internal activation key that is encrypted. You can either hide it someplace on the system making it difficult to find, or as I do for the CSDA Code Utility (http://www.csda1.com/csda_codeutility/CSDA_CodeUtility.html), create a public key that is almost impossible to generate without knowing the code.

Duplicate Usage
Note that if a user duplicates the environment of the licensed computer (e.g. the same machine name) it allows the software to be used on that duplicate machine.

One way to stop a duplicate machine is to not allow sharing of a table or other common file/resource that all computers running the software must be required to use. This really can't be done, unless the usage of the application requires sharing the resource.  You can't share a table from two computers in Windows if you have the same machine name, but if they run two totally different applications or networks, it can't be stopped.

Some software stops the duplicate by actually having the software check in with a web site on a regular basis, sending machine specific data to validate the license.  This could be every time, however, if you lose your internet connection, it will fail.  So some software are set to stop working if there is not valid check in at the website within a certain amount of time. The problem is that you must have a good validation web server working 100% of the time for as long as you have users still using it (or set it such that if the web site page is accessible (easy to keep up), but the server is not, then it also runs.

In terms of basic concepts, that's pretty much it. You have to decide the level of protection you are looking for. 

When I do use an md5 hash, I never hash the plain password. You should always use a "salt" as part of your hash to protect against dictionary attacks and other ways to break the passwords. For a simplified example, instead of md5("MyPassword"), I would do md5("this is some salt text that only I know" + "MyPassword"). And you would never want to repeat this algorithm on the client because then your additional measure is fully exposed and defeated.

Typical passwords today have to be 6+ in length, contain a digit not as the first or last character, not contain their login ID, and a few other rules.

Also look at my tips on Data Security. While I talk about data, it also applies to license keys etc. My license keys for my utilities are MD5 encoded and I use various other techniques to make them hard to recreate even if looking at the source code. By creating a license # based on the license # and other machine specific values such as machine name, Alpha serial #, Alpha version # and types, etc it can be made pretty hard to copy or share on a network.

Alpha Five Version Release Dates (Email Comments) Updated 20 July 2013

To give you an idea of when a program was developed, these are the release dates and build numbers of Alpha 5 through Alpha 11. Note that the system build #'s do not follow a consistent pattern, however the Addin Library build # (the 2nd pair of 4 digits) consistently increases by date and version.

Most recent released version 12 is 1507_4150 dated 20130625
1st  released version 12 is 1366_4115 dated 20130421
1st Beta released version 12 after v11 primary development stopped is 1289_4098 dated 20130326
1st  beta released version 12 is 1241_4080 dated 20130310

Latest Beta release version 11 after v12 release 3662_4155 dated 20130718
Last released version 11 is 3381_4096 dated 20130322
1st  released version 11 is 2199_3810 dated 20111020

(Note: Version 10.5 uses the same license as Version 10)
Last released version 10.5 is 4369_3712 dated 20110611
1st  released version 10.5 is 2924_3560 dated 20100610

Last released version 10 is 2814_3539 dated 20100316
1st  released version 10 is 2515_3388 dated 20091015

Last released version 9 is 2095_3264 dated 20090310
1st  released version 9 is 1389_3163 dated 20080327

Last released version 8 is 1980_3163 dated 20080327
1st  released version 8 is 1269_3059 dated 20070228

Last released version 7 is 4130_3033 dated 20060814
1st  released version 7 is 3872_3001 dated 20051027

Last released version 6 is 1600_2057 dated 20050711
1st  released version 6 is 1530_2001 dated 20040726

Last released version 5 is 1506_1058 dated 20040309
1st  released version 5 is 1242_1001 dated 20020905

QReportBuilder Release Info
Most recent released QReport Builder 2012 is ????
1st  released QReport Builder 2012 is ????

Last released QReport Builder 2011 is 4335_3709 dated 20110603
1st  released QReport Builder 2011 is 2924_3560 dated 20100610

Last released QReport Builder 2009 is 2193_3268 dated 20090507
1st  released QReport Builder 2009 is 1600-3196 dated 200xxxxx???

Last released QReport Builder 2008 is 2029_3183 dated 20080515
1st  released QReport Builder 2008 is 1804_3115 dated 20071005

Using Dates in databases (Email Comments) Updated 06 April 2012

Alpha Five has a very large number of date and time conversion and manipulation functions.

When using DBF files (dBase III combatible), 4 data types are relevant, which are
You should use one of the 1st 3 data types for storage of most date and time information. The numeric type is probably never really useful unless you are just storing the year.  However, if you ever want to store a partial date, the character data type is generally best.

An example are credit cards that typically have expiration dates of just the year and month. You could store it as a date type with a day of the last day in the month, and ignore the day of the month portion as needed in other expressions.  Alternatively you could store just the year and month as a character string.

Another example is a birthdate.  Some people may only give some portion of the date, typically either leaving out the year, or leaving out the day and month.  If you are storing a date that only partial information is available, store it as a character string, leaving blank the unknown portions. If you ever obtain the additional info, you can fill it in.

Format of a Date stored in a character string
When you create a date in a character string in just about any database (or other places such as a file name), you should generally store the full date in the format of YYYYMMDD.  If you don't need the days, then YYYYMM or for year only, YYYY.  

If you want, you can extend the format with time, as in YYYYMMDDHHMMSS

In any case, make sure you always store the full 4 digit year, otherwise, you are not Y2K compliant, and there can be issues as to what century appies to a date.

These concepts applies when using a date in file naming, data storage (in character form, however Date, and Time fields already store it this way internally) and usage in index or sort expressions.  Only for display or entering a date and/or time, should you change the format to a human-readable format .

The main reason for this it automatically orders the data by year, then month then day.  If used in a filename, particularly at the beginning, it allows easy sorting by date by sorting by the filename.

Another reason is the format is not specific to the United States (format mm/dd/yyyy), or most of Europe (format dd-mm-yyyy), but is a format that everyone can use.

Placing dates in a filename allows a sequence of similar filenames to be sorted by date.  You could use the file Create date, or file modify date to sort but both of these dates can get modified with some copy and archiving procedures. File modify dates can be changed every time you open it up in some editors. By placing the date in the filename, it is less likely the date will change.  

If you go to the Alpha Five control Panel, and select from the menu, View, Settings..., then go to Preferences, then Advanced followed by Archive Reports File Name, it will show you one example where Alpha Five allows setting the format for the date/time portion of a file name. This is used for saving the results of a layout (form, browse, label, letter, report) to a file.  But this is not used everywhere within Alpha Five.

Some examples of good file naming formats with dates in them are:

This case allows sorts by all the files with the same "filename" and then by date

This case is just an extension of the above, and allows sorts by all the files with the same "filename" and then by date, descriptions don't really matter for the sort

This case allows sorts by all dates 1st, then by filename grouping all dates together

Date Input Format
Certain date functions use the current character date format of mm/dd/yyyy (American) or dd/mm/yyyy (European) or possibly another format which can depend upon the Windows default date settings, or an Alpha Five date settings.  If you want your XBasic code to be independent of this, you must either use code to handle the particular cases, or use a date function that works independent of this setting

To detect which format you are using, you can use IF expressions similar to this
= "American"
= .T.
Note that the date constant above never needs to be changed, it is basically returning whether the 1st or 2nd part of the date constant is being interpreted as a day of the month within Alpha Five.

Date constants are evaluated based upon the default setting format, so a date constant of {03/04/2012} will always yield a character version of the date with DTOC({03/04/2012}) as "03/04/2012", but which part is the day or month?  You can't really tell.

Internally the date is stored in effectively a yyyymmdd format, so once you have a date in a date type variable, or stored in a date field, it's value will always be that date value, even if you change the Date default format setting.  The meaning when you 1st created the value will stay, even if the default setting format changes.

But in Alpha Five, date type values are inputted in the current date default setting format. When you specify a date constant of {ww/xx/yyyy} where ww and xx will be interpreted as a day or month depending on the format. Similarly, if you input into a field, the stored date value will depend on the current date default setting.

If you specified a date constant in an expression such as DAY({03/04/2012}), it will return 4 in American format, but a 3 in European format.  Similarly, MONTH({03/04/2012}), will return 3 in American format, but a 4 in European format. CDATE({03/04/2012}) will also return different results.  In America you will get "20120304" but in European, "20120403".  But these different values are because the date value stored by the date constant {03/04/2012} was different based upon the settings. It was not the function, but the conversion from input of the constant based upon the format setting to the internal date format.

Had you instead used CTOD("03/04/2012") to convert the string constant, you would have found that the date stored would have been March 4th, 2012, or April 3rd, 2012 depending upon the current date default setting format.

But once a date is stored correctly in a date type variable or field, that date is that date.  One of the best ways to store date values that is format independent is using code similar to this
dim datevalue1 as D
Since STOD() always use a string format of yyyymmdd, the date value is always what you want. DAY(datevalue1) will always return a 4 in the above code

Note many Alpha Functions as well as date fields on a layout have a display format for a date, but this is not the format you actually enter the date in or specify in a constant.  The input format will always be the default date setting format, but once you stop inputting the field (by leaving focus of the field, or by saving the record), you will see the display format.

Windows 7 (and Vista) compatibility (Email Comments) Updated 09 February 2013

To install in Windows 7
A5v10 and a5V11 can just be installed and used normally. For earlier versions of A5, use the following procedures:

Windows Automated way

  1. Right click on a5vX shortcut icon and select Troubleshoot compatibility
  2. Select "Try recommended settings"
  3. Then select Start the program... and then exit A5
  4. Select Next
  5. Select Yes, save these settings for this program
  6. Select Close
Note that in earlier versions of A5, it will suggest Windows XP (Service Pack 2), but Windows XP (Service Pack 3) is probably better to use.

Windows Manual way (Faster and easier to do)
  1. Right click on A5v9, A5v8, a5v7 or a5v6 icon and select Properties
  2. Go to Compatibility tab
  3. If you want to use these for only the current user then change this page, otherwise Select "Change settings for all users"
  4. check box Compatibility mode and set pull down to "Windows XP (Service Pack 3)"
  5. Optionally, check box "Run this program as an administrator".  This seems to do nothing in terms of the UAC popup, but may be helpful to access some folders that may otherwise be restricted.
Even though this seems to basically works, some testing of A5v7 runtime applications required UAC to be totally off for some operations to work.  So if you experience any issues running this way, turn off UAC on each system using it.  Other versions up through A5v10.5 may also experience issues along these lines, so try lowering the UAC level until it works (which may in fact require it to be totally off).

The earliest releases of A5v5 seems to almost work except 4 drivers don't install properly and menus may not appear. After installation, install the latest A5v5 patch, and the 4 drivers that have errors will go away. If the menus don't appear, then you have one of two choices:
  1. If you have menu and/or toolbar display issues within A5V5 when you start, you can try this;
    • Start Windows 7 Control Panel
    • In the top right search box, type "Performance"
    • Select "Performance Information and Tools".
    • Select "Adjust Visual Effects"
    • Choose "Custom"
    • Scroll down to the bottom and uncheck "Use visual styles on windows and buttons"
    • Press "Apply" button
    Restart Apha 5 Version 5

    Note: The display of windows desktop and other programs will have a changed look in the buttons and menus as a result of this setting.
  2. Install and run a5v5 in a "Windows XP mode" virtualized window. To learn more how to install Windows XP mode, see here

Windows XP mode operation
In Windows XP mode window, A5v5 through A5v11 all install and operate fine, with the possible exception that A5v7 may have an issue with a driver.

Theory of Deduping (Email Comments) Updated 11 Apr 2013

If you have data, it is almost impossible not to have data that has multiple copies of the same data. This often comes from importing data that has duplicate data to what you already have, or a 2nd manual entry of the data, when it is not realized hat a record already exists.

Even if you have a key like Social Security Number (SSN) that is unique to each record, occasionally a mistake entering the data, possibly due to not reading the crawled handwriting, transposing 1 or more characters, or just a transcription error, may, on occasion result in 2 or more records with the key.

So you may think, I'll just create a unique key index, so a second copy can't be entered.  Let's suppose that the 1st SSN is typed in incorrectly from what it really is. If no one else has that same wrong SSN, it will accept the record save. Later, another entry from the same person is now typed in with the correct SSN.  The 2nd record will be saved as well, creating a 2nd record representing the same person. So it is very possible, even with a unique key, you can get duplicate records if there is a typing mistake on the unique key fields of the data. And if you don't have a really unique field, you may end with, e.g. multiple John Smiths in the data, where many are really the same.

So identifying duplicates is important at entry time, as well as later.  This leaves us with the problem of how to remove the duplicates, or "Deduping".  There is seldom any 1 single method that can allow for deduping, and it is very data dependent.  It depends on what data you have/collect, and how clean the data is.

E.g. unclean data would have birthdates in the future, or over 110 years in the past (from when the record was created assuming it's not a historical list).

So the 1st steps are to clean your data as best as possible.  Some of these include
To dedup your data records, the best way is to have a variety of fields that either individually or as a group create different unique keys of a record. That way, if there is a mistake in a field, or portion of a field, the likelyhood of a data error will be caught by one of the groupings of unique data.

More to be added later

Easier Customer Support Techniques (Email Comments) Updated 01 May 2013
If you have an application that others are using, being able to support it is a primary concern.

There are things you can change in your application that can make it easier to support your users.
1. All forms, browses, reports etc should have a code number in some location (e.g. lower right corner) that is unique to it.  That way when some user says they pressed the print button on a form, you can get information as to what form.
2. You can add error handling to code to display relevant information, e.g. script/function name, line number, error #, and maybe some of the key calling parameters, so the user can report these back to you.  Alternatively, you can write this data to an error log file that you can then examine after the failure.
3. Somewhat more difficult, you can create a breadcrumb variable that concatonates the path that the user took to arrive at the button as it goes from one menu level to another so that you would have a path, then write this to an error log file
4. Create a transaction file that shows the various buttons, forms, and operations, user, date/time, etc that are being run, and maybe include error info as well.  You may want to limit it to only the last x number of days or maximum size of the file, throwing away the older data.

Alpha Software Value Added Reseller If you haven't yet upgraded to
Alpha Five Version 11, now's the time to
download the free Alpha Five trial as well!
Alpha Five Version 8 box