Recompile any object files under
the version of dBASE you will be using. While not always necessary for proper operation,
it is always recommended. This will optimize the code under the dBASE version running and
help avoid potential problems.When going from v1.1
to v1.5 ( or anytime you move programs from one version to another ) make backups and then
delete all object files,
DELETE *.??O
If there is no object file and the source files still
exists, dBASE will automatically do a recompile the next time you run your program. Or you
can recompile the code manually:
source object
.FMT -> .FMO SET FORMAT TO <filename>
.FRG -> .FRO REPORT FORM <filename> WHILE .F.
* WHILE .F. suppress report output to screen
.LBG -> .LBO LABEL FORM <filename> WHILE .F.
.QBE -> .QBO SET VIEW TO <filename>
.PRG -> .DBO COMPILE <filename> [RUNTIME]
* RUNTIME switch checks
* RUNTIME/EXE compatibility

* dBIV 1.1-1.5 Differences * This also includes v1.1 ->
v2.0 *
The SELECT() functions has been changed in 1.5. In 1.1 it
returned the HIGHEST available work area, in 1.5 it returns the LOWEST.(see pg 4-127 of LR
and page 17-4 of 'Getting Started'). The reason it has change is due to more work areas
and more efficient use of memory. In v1.1 you had ten work areas and each was allocated a
section of memory. In v1.5 with 40 work areas dBASE now allocates the work area as you use
them, hence SELECT() pulls the next Available work area.
Assume you're in work area 1, the default, with no DBFs
open:
USE table3 IN SELECT()
USE table2 IN SELECT()
USE table1
Table3 is opened IN 1, table2 IN 2, and then the USE table1
command closes table3 and opens table1 IN 1. In pre v1.5 versions, table3 and table2 would
have been opened IN 10 and 9 respectively.
-----------------------------------
Because not all workareas are opened at startup in v1.5,
CATALOG is no longer strictly in area 10. In v1.5 you can use the CATALOG() function for
accessing the catalog directly.

Duplicate checking via .NOT. SEEK(FieldName) under Accept
Value When in a screen form; this worked in 1.0 and 1.1 but not under 1.5. It should never
have worked or been suggested as a means of achieving the goal of prevention of duplicate
entries, but unfortunately many people have it in their screen forms. It should not be
used because you don't want to move the record pointer in the current database while
APPENDing/EDITing.
Version 1.5 handles the phantom record a little differently
than 1.1 did. Essentially, if you move the record pointer ( as will occur with a
validation seek UDF ), it 'forgets' the values of the GET's prior to and including the GET
to which the seek is attached.
The following is an excerpt from the READ.ME from dBASE IV
ver 1.5.
" If you write code or use an application that moves
the record pointer during an interruption, (see the item above), you must take special
care when you're executing the interrupt during the full-screen APPEND command. Whenever
you interrupt APPEND, you should restore the record pointer as mentioned in the previous
item. In addition, your interrupting code must save (on entry) any values entered onto the
APPEND screen prior to the interruption. You must then restore these saved values after
the record pointer is repositioned (just before the interrupt routine exits). These steps
are required because dBASE IV keeps all values entered during the APPEND command in a
temporary buffer. During APPEND, the buffer record values are handled as follows: - If you
press Esc and abandon the new record, the values are discarded. - If you accept the new
record during a normal APPEND, the values are added to the database file as a permanent
record. However, when the record pointer is moved during an APPEND interrupt, the values
in the temporary record are not retained (they become blank). Although the APPEND screen
may show the values as they were originally entered, those values are no longer in the
temporary buffer. A common occurrence of this is during an interrupt call to a validation
UDF which uses a SEEK to check for duplicate keys. SEEK will reposition the database,
changing the current record pointer and blanking the temporary buffer. "
To check for duplicates you can use one of the following
methods:
@ 5,5 GET fld_name ;
VALID REQUIRED nodup()
READ
FUNCTION Nodup
mselect = SELECT()
USE employee order lastname in (mselect) AGAIN
mreturn = .NOT. SEEK(lastname,mselect)
USE IN (mselect)
RETURN mreturn
If you have v2.0 or dB5D you can use KEYMATCH() to check
for duplicates. It will not move the record pointer and will use any existing index for
very fast searches!
@ 5,5 GET fld_name ;
VALID REQUIRED ;
(.not. KEYMATCH(fld_name,TAGNO("tagname")))
* you can use the index number as an argument
* within KEYMATCH() so you don't have to
* SET ORDER TO <tagname> prior to using
* TAGNO("tagname") returns the index
* number.
READ
For more on using KEYMATCH() see
Lib 10: TI1641 Preventing Duplicates in a Data Entry Screen.

Files that use aggregate operators in the source file work
in 1.1 and fail in 1.5.
USE x-y && a filename with a dash
? ALIAS() Version 1.1 returns: A
1.5 returns: _1
If you try to use this filename in an expression, ( or
dBASE makes a temporary file during a query for example ) Version 1.5 generates the error
ALIAS NOT FOUND. If you follow proper naming conventions, you will avoid these types of
errors.
Naming conventions for field, file and memory variable
names:
BEGINS with a letter, and contains nothing but letters,
numbers and the underscore "_" character ( Don't use characters such as
#,&,-,etc. ). Do not use dBASE reserve words. These are dBASE command words or the
first 4 letters of a dBASE command as a name. Example: TOTAL, TOTA; SELECT, SELEC, SELE.
Don't use the letters A-J, these are reserved for ALIAS names.

*** dBIV 1.1/1.5 - 2.0 Differences ***
PAD() RETURNS INCORRECT VALUE FROM A SUBMENU. DIFFERENT
FROM V1.1/V1.5 Problem with PAD() in v2.0
DEFINE MENU TOPLEVEL
DEFINE pad PAD_1 OF TOPLEVEL PROMPT "a1" AT 0,2
ON SELECTION pad PAD_1 OF TOPLEVEL DO PROC_1
DEFINE pad PAD_2 OF TOPLEVEL PROMPT "b1" AT 0,18
ON SELECTION pad PAD_2 OF TOPLEVEL DO PROC_1
DEFINE MENU amenu
DEFINE pad PAD_1 OF amenu PROMPT "a2" AT 0,2
ON SELECTION pad PAD_1 OF amenu DEACTIVATE MENU
DEFINE MENU bmenu
DEFINE pad PAD_1 OF bmenu PROMPT "b2" AT 0,2
ON SELECTION pad PAD_1 OF bmenu DEACTIVATE MENU
ACTIVATE MENU TOPLEVEL
RETURN
PROCEDURE PROC_1
DO CASE
CASE PAD() = "PAD_1"
ACTIVATE MENU amenu
CASE PAD() = "PAD_2"
ACTIVATE MENU bmenu
ENDCASE
RETURN
WORKAROUND: Moving the menu prompt will correct this
problem, so use KEYBOARD right, left arrow.
PROCEDURE PROC_1
DO CASE
CASE PAD() = "PAD_1"
ACTIVATE MENU amenu
CASE PAD() = "PAD_2"
ACTIVATE MENU bmenu
ENDCASE
KEYBOARD CHR(4) + CHR(19)
RETURN

For additional changes between versions see the 'What's
New' section in Getting Started manual and the README file.
There are also the following related files in library 1:
V15NEW.ZIP
Title : New features in dBASE IV v1.5 DBASE IV V1.5 New features, compatibility, questions
& answers
V20NEW.ZIP
Title : Changes in dBASE IV v2.0 This file lists the changes in the newest version of
dBASE, dBASE IV v2.0