Liberty Basic is develeopped by Carl Gundel Original Newsletter compiled by Alyce Watson and Brosco Translation to HTML: Raymond Roumeas
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:
By Dean Hodgson copyright (c) 1999 dhodgso-@nexus.edu.au
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
Comments, requests or corrections: Hit 'REPLY' now!