Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas

The Liberty Basic Newsletter - Issue #35 - MAY 99

"Knowledge is a gift we receive from others." - Michael T. Rankin

In This Issue:

Part Three of a multi-part series on Disk File Functions by Dean Hodgson. Brosco and Alyce wish again to thank Dean for this OUTSTANDING series!

If you also learned from this article, why not comment here or write directly to Dean:

mailto:dhodgso-@nexus.edu.au

In Future Issues:


DISK FILE HANDLING IN LIBERTY BASIC

By Dean Hodgson copyright (c) 1999 dhodgso-@nexus.edu.au

Part 3 - Windows API disk file commands

Windows has its own set of API disk file handling functions. They work very differently to LB's own field commands and they allow you to do some things that LB itself does not permit such as sharing networked files and reading or writing data to specific parts of a file.

 First, you need to open the API Dynamic Link Libraries (DLL):

OPEN "kernel" for DLL AS #kernel
OPEN "user" for DLL AS #user
 
Remember to close these at the end of your program with
CLOSE #kernel
CLOSE #user

GetDriveType

This API function can tell you the type of drive that is available.

CheckDrive = 1
CALLDLL #kernel, "GetDriveType", _
CheckDrive AS short,_
result AS word

CheckDrive is a number indicating the drive where 0 is for drive A, 1 is for drive B, 2 for C, etc. The result is a number that indicates the type of drive:

2 floppy disk
3 hard disk
4 network drive

You don't have to insert a disk for this function to work. A Cd-Rom drive often returns a value of 4.

SetErrorMode

You call this API function before doing any of the others below. And you must call it again afterwards. This functions turns on/off Windows own file error messages.

ErrorMode = 1
CALLDLL #kernel, "SetErrorMode", _
ErrorMode AS word,_
result AS word

Setting ErrorMode to 1 turns off Windows error messages. What then happens is the API calls below all return an error value. Setting ErrorMode = 0 turns Windows error messages back on. The result can generally be ignored.

As an example, after doing the above, you can try to create a file on drive A. Do not put a floppy disk in when doing this and you'll see the error. The program is:

OPEN "kernel" for DLL AS #kernel 'open the API libraries
 
ErrorMode = 1 'turn off Windows error trapping
CALLDLL #kernel, "SetErrorMode",_
ErrorMode AS word,_
result AS word
 
File$ = "A:\TEST" 'going to try to create a file
Attrib = 0
CALLDLL #kernel, "_lcreat",_
File$ AS ptr,_
Attrib AS short,_
result AS short
 
IF result = -1 THEN
NOTICE "An error happened accessing DRIVE A:"
ELSE
KILL File$
END IF
 
ErrorMode = 0 'turn error trapping back on
CALLDLL #kernel, "SetErrorMode",_
ErrorMode AS word,_
Result AS word
 
CLOSE #kernel

FILE EXISTS TEST

The API call _lopen can be used to see if a file exists. This function is described below.

File$ = "TEST" 'filename we are checking for
Type = 16384 'use this value to test for a file
CALLDLL #kernel, "_lopen",_ 'try to open the file
TestFile$ AS ptr,_
Type AS short,_
result AS short
 
IF result<>-1 THEN NOTICE "File Exists" ELSE NOTICE "File Doesn't Exist"

The result should be 0 if the file exists.

CREATING A FILE _lcreate

Unlike LB's commands, the API makes a difference between creating files and opening already existing files. This means you must check for the existence of a file before opening it, if you do not know it is present.

Attrib=0
CALLDLL #kernel, "_lcreate",_
File$ AS ptr,_
Attrib AS short, _
result AS short

Attrib is a value indicating the type of file:

0 normal read/write file
1 read only file
2 hidden file
3 system file

The result is a number assigned by Windows to the open file. If this value is -1, an error has happened.

Note: If you attempt to create an existing file, all the contents of that file are lost!!

OPENING A FILE _lopen

The API does not differentiate between Sequential, Random and Binary files. All files are essentially Binary when opened but they have certain read/write properties. _lopen is used to open a file that has been created. If you try to open a file that does not exist, the result will be an error.

FileType=2
CALLDLL #kernel, "_lopen",_
File$ AS ptr,_
Type AS short,_
FileHandle AS short
NOTICE "File handle for "+TestFile$+" is "+STR$(FileHandle)

The result of _lopen is a number called the "File handle". Windows assigns this value to an open file, unlike LB's commands where you assign the handle. You then use the FileHandle value in all subsequent operations on the open file. The value is different each time you open the If there was an error opening, FileHandle is -1.

The FileType is important and offers functionality not found in LB's OPEN statement. Here are some of the values you can use when opening files.

0 is read only users can only perform read operations on the file
1 is write only users can only write to the file
2 is read/write users can either read or write to the file
64 is shared read only allows network shared access for reading only
65 is shared write only allows network shared access for writing only
66 is shared read/write allows network shared access for reading and writing

Under normal circumstances, you should set the FileType to 2 for use on stand-alone computers or 66 for use in a network situation where more than one user can access the file at the same time. If you don't know the situation in which your software will be used, 66 is a reasonable default.

If a file has been opened using a 0 and another computer on a network tries to open the same file, they will get an error. If the file is opened using 64 and another user tries to open with a 64, no error will occur and both machines will be able to read from the file at the same time. However, if another user tries to open the same file with a different value, they will get an error.

WRITING TO A FILE _lwrite

The function _lwrite can be used to write strings up to 64k long to a file.

S$="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
LengthOfString=LEN(S$)
CALLDLL #kernel, "_lwrite",_
FileHandle AS short,_
S$ AS ptr,_
LengthOfString AS short,_
result AS short

The exact string will be written to the file as is. No end-of-line markers (CHR$(13) CHR$(10)) are written. If you want these, you need to add them to the string yourself before writing and include them in the length.

FileHandle is the value returned by _lopen.

LengthOfString is the number of bytes to write. If this value is less than the actual length of the string, then only that number of characters are written. If the value is greater then an error occurs.

SEEK _llseek

The API contains a function for positioning the 'file pointer' to any spot within a file. This pointer specifies where you will start reading or writing data. Note: the pointer is automatically moved to the end of any data that is read or write by _lread or _lwrite. _llseek is used to move the pointer to another position within the file.

Position=5
Option=0
CALLDLL #kernel, "_llseek",_
FileHandle AS short,_
Position AS long,_
Option AS short,_
result AS long

Position is the number of bytes to move the pointer to the beginning of a record by multiplying the record length by the record number -- 425 * 6 puts the pointer at the start of record 6. Note: that record numbers start with 0 and not 1!!

READING FROM A FILE _lread

This is similar to _lwrite. However, you need to set up a buffer string in order to receive the data. In the example below, we are going to read 10 bytes from the file. Therefore the buffer needs to be at least 10 bytes long. We also need to add a CHR$(0) at the end to tell LB to deal with this string properly within the DLL call.

Buffer$ = SPACE$(10) + CHR$(0)
Readbytes = 10
CALLDLL #kernel, "_lread",_
FileHandle AS short,_
Buffer$ AS ptr,_
Readbytes AS short,_
result AS short

The value in Readbytes should not be greater than the length of Buffer$! If an error happens, the result will be -1, otherwise it will be the number of bytes read.

CLOSING THE FILE _lclose

CALLDLL #kernel, "_lclose",_
FileHandle AS short,_
result AS short

The result will be 0 if the file was closed properly.

CLEANING UP

Remember to turn error message reporting back on.

wMode = 0
CALLDLL #kernel, "SetErrorMode", _
wMode AS word,_
result AS word

And, of course, to close the API DLL files

CLOSE #kernel
CLOSE #user

An example:

Here is a random access file example using just the API disk calls. It does the same thing as the example given for LB in the previous part. In order to make the functions "general purpose" and reusable, I've put them into subroutines. Also, those readers clever enough may be able to see how to use structs to hold record data instead of the method I've employed.

OPEN "kernel" for DLL AS #kernel
OPEN "user" for DLL AS #user
 
GOSUB [ErrorOff] 'turn off Windows errors
File$="TEMP.DAT" 'The filename
GOSUB [CreateFile] 'Create file (it'll be open)
GOSUB [CloseFile] 'Close it
 
GOSUB [OpenFile] 'Open the file
A$="123" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field, first record
B$="456" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field, first record
A$="789" : NBW=10 : lwrite$=A$ : GOSUB [Write] 'first field, second record
B$="ABC" : NBW=5 : lwrite$=B$ : GOSUB [Write] 'second field, second record
NBW=15 : lwrite$="" : GOSUB [Write] 'blank record at end
GOSUB [CloseFile]
 
recsize=15
File$="TEMP.DAT" : GOSUB [OpenFile] 'open file
print "Record one"
seek=0 : GOSUB [Seek] 'put pointer at record 0
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field
NBR=5 : GOSUB [Read] : B$=TRIM$(lread$) 'read second field
PRINT A$
PRINT B$
PRINT "Record two"
seek=1 * recsize : GOSUB [Seek] 'pointer to 2nd record (1)
NBR=10 : GOSUB [Read] : A$=TRIM$(lread$) 'read first field
NBR=5 : GOSUB [Read] : B$=TRIM$(lread$) 'read second field
PRINT A$ 'show fields
PRINT B$
GOSUB [CloseFile]
 
GOSUB [ErrorOn] 'clean up
CLOSE #kernel
CLOSE #user
END
 
'Opens a file in shared read/write mode
'Pass File$ as the filename
'Returns FileHandle. 0 if an error.
[OpenFile]
FileNumbere=1
CALLDLL #dl,"DOPENR",_
FileNumber AS short,_
File$ AS ptr,_
recsize AS short,_
result AS short
RETURN
 
'Read string from file
'NBR is number of bytes to read
'Result is returned in string lread$, which does not have spaces
'truncated.
'Result is -1 if an error
[Read]
lread$=""
temp$=SPACE$(NBR)+CHR$(0) 'temporary string to receive data
CALLDLL #kernel, "_lread", FileHandle AS short, temp$ AS ptr, NBR AS
short, result AS short
IF result>=1 THEN lread$=LEFT$(temp$,result) 'strip 0 at end of string
RETURN
 
'Writes the string lwrite$
'NBW is the number of bytes to write
[Write]
temp$=lwrite$ 'make it a temporary string
temp=NBW-LEN(temp$) 'test to see if string isn't
right length
IF temp>0 THEN temp$=temp$+SPACE$(temp) 'too short, add spaces
IF temp<0 THEN temp$=LEFT$(temp$,NBW) 'too long, truncate
CALLDLL #kernel,"_lwrite",FileHandle AS short,temp$ AS ptr,NBW AS
short,result AS short
RETURN
 
'seek file pointer, the number of bytes from the start of the file
[Seek]
temp=0
CALLDLL #kernel, "_llseek", FileHandle AS short, seek AS long, temp AS
short, result AS long
RETURN
 
[CloseFile]
CALLDLL #kernel, "_lclose", FileHandle AS short, result AS short
RETURN


API DISK FILE LIMITATIONS


Newsletter compiled and edited by: Brosco and Alyce.

Comments, requests or corrections: Hit 'REPLY' now!