Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
Low-level File Functions
Low-level File Functions
(LLFFs) are cool. They can be a hacker’s best friends, and are certainly essential tools in our toolbox. With LLFFs, you can open and read all those nasty binary files that the operating system and manufacturers would prefer you didn’t take apart. With LLFFs, it’s possible to translate a Windows Cardfile into a .DBF, read and rewrite a FoxPro macro file, translate the Screen memory variable stored in a .MEM file, or study and perhaps rearrange the FoxPro codepage sort logic (FOXPRO.INT).
See the disk for the result of a few hours of taking apart the .FKY keyboard macro function file and analyzing the results: PeekFKY.PRG dumps the contents of a macro file to the screen. Check out the topics SAVE MACROS
and RESTORE MACROS
, and think about what you could do with keyboard macros being updated programmatically.
Low-level File Functions
follow a logic that will be familiar to users of other languages, but may be a little different for FoxPro devotees. Rather than referring to a file by its alias, a file “handle” number is created using FOPEN()
or FCREATE()
. A file pointer, like a record number, determines where the next bytes are read or written to. The FSEEK()
function allows us to move this file, repositioning the pointer relative to the current position or to the top or bottom of the file. FREAD()
or FGETS()
reads data from the file, either byte-by-byte or by lines, and FWRITE()
or FPUTS()
matches the reading functions with their equivalent outputs. Finally, a few miscellaneous functions allow flushing (FFLUSH()
), end-of-file testing (FEOF()
) and error checking (FERROR()
).
Low-level File Functions
were the most common ways to manipulate files that were not in DBF format, but there are several other techniques, some new to Visual FoxPro. These techniques may have advantages over LLFFs for your task, and bear consideration. If you are working with ASCII text, consider sticking the file in a memo field with APPEND MEMO
and using the memo field functions MLINE()
and MEMLINES()
to hack it apart, or an APPEND FROM
TYPE SDF to pull it into multiple records. If the text files are in the Windows INI format, consider the WinAPI calls described in “INI Files
.” New to VFP 6 are the StrToFile()
and FileToStr()
functions to load an entire file into one character variable in one swoop.
If you’ll be writing out a text file, consider SET ALTERNATE
and SET TEXTMERGE
as well as using LLFFs.
But for hacking binary formatted data, there’s nothing better than the Low-level File Functions
. Let’s see what they can do.
FCHSIZE()
FCHSIZE()
modifies the size of a file on disk. This command can increase, decrease or zero-out the size of a file.
nNewSize = FCHSIZE( nFileHandle, nSize )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
nSize |
Numeric |
Final desired size. |
nNewSize |
–1 |
An error has occurred; see FERROR() for more information. Check for disk space, file opened with write access, or network rights issues. |
Numeric |
The new size of the file. |
lnRetVal = FCHSIZE(8, 0)
The above example zeroes out the length of the file opened with file handle 8.
FCLOSE()
FCLOSE()
closes a file opened with Low-level File Functions
.
lSuccess = FCLOSE( nFileHandle )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
lSuccess |
.F. |
An error has occurred; see FERROR() for more information. Check for the correct file handle, network rights. |
.T. |
File successfully closed. |
Note that issuing the CLOSE ALL
command closes all files opened with the Low-level File Functions
.
* Close the file opened with file handle 8.
llRetVal = FCLOSE(8)
FCREATE()
FCREATE()
creates a file on disk, and returns a “handle”—a numeric value that other Low-level File Functions
must have to refer to the same file.
nHandle = FOPEN( cFilename [, nAttribute ] )
Parameter |
Value |
Meaning |
cFilename |
Character string |
The full path and filename of the file to be opened. |
nAttribute |
0 or omitted |
Read-Write. Any option other than the default 0 parameter prevents FoxPro from writing to the file. |
1 |
Read-only. |
|
2 |
Hidden. |
|
3 |
Read-only and hidden. |
|
4 |
System. |
|
5 |
Read-only and System. |
|
6 |
System and Hidden. |
|
7 |
Read-only, Hidden and System. |
|
nHandle |
–1 |
An error has occurred; see FERROR() for more information. |
Positive integer |
File successfully opened. |
* Create the AUTOEXEC.BAT files to be read/write.
lnHandle = FCREATE("C:\AUTOEXEC.BAT", 0)
If you create a file and forget the handle number, such as by issuing the command =FCREATE(“test”,0), you can determine the file handle of all open files with the DISPLAY STATUS
command.
No warning is given to overwriting an existing file, even if SAFETY is SET ON!
FEOF()
FEOF()
tests for end-of-file on a file opened with Low-level File Functions
.
lAtEof = FEOF( nFileHandle )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
lAtEof |
.F. |
Not at end-of-file. |
.T. |
At end-of-file. |
* Check for end-of-file for the file opened with file handle 8.
llRetVal = FEOF(8)
FEOF() returns .T. if passed an invalid file handle. Neither a FoxPro error condition (like error 1113: "no file is opened in that area, stupid") nor a low-level file error (see FERROR(), for example 6: "Invalid file handle") is generated. |
Low-level File Functions
error-handling is funny but useful. Rather than use FoxPro’s built-in error generation and the language’s error-handling capability, LLFFs set an error condition detectable by FERROR()
. This allows local testing of the success or failure of LLFF execution, where a global error-handling method may not be appropriate. No arguments are accepted.
nErrNum = FERROR()
Error Number |
Meaning |
2 |
File not found. |
4 |
Too many files open (limited by file handles). |
5 |
Access denied. |
6 |
Invalid file handle given. |
8 |
Out of memory. |
25 |
Seek error (usually top of file). |
29 |
Disk full. |
31 |
Error opening file. |
nFileHand = FOPEN("NUL:")
if nFileHand < 0 && negative means error
do case
case FERROR() = 2 && File Not Found...
FFLUSH()
flushes data to disk.
lSuccess = FFLUSH( nFilehandle )
Parameter |
Value |
Meaning |
nFilehandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
lSuccess |
.T. |
FLUSH successful. |
.F. |
Error. |
llRetVal = FFLUSH(8)
The above example flushes all data to disk for the file opened with file handle 8.
FGETS()
FGETS()
reads data from a low-level file. FGETS reads characters until it reaches the first of these three limits: the number of characters specified in the second parameter, the first (next) occurrence of a carriage return, or the end of the file.
cString = FGETS( nFileHandle [, nBytes ] )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
nBytes |
Numeric |
Maximum number of bytes to read; defaults to 254 if not specified. |
cString |
Character string |
Characters read. |
lcRetVal = FGETS(8,400)
The above example reads characters from file handle 8, starting at the current file position, and ending at the first occurrence of a carriage return, the specified number of bytes, or the end-of-file (hint: test FEOF()
).
FOPEN()
opens a file on disk for reading and/or writing, and returns a “handle”—a numeric value that other Low-level File Functions
(LLFFs) must have to refer to the same file. While it may be theoretically possible to open other DOS devices, such as “LPT1:” or “COM1:” with this command, results under Windows are less than 100 percent predictable (and get less predictable under Win98 and NT!). A far better solution to serial port manipulation is to use the Win API function calls (i.e., OpenComm()) or a third-party library specially designed for serial-port work.
nHandle = FOPEN( cFilename [, nMode ] )
Parameter |
Value |
Meaning |
cFilename |
Character string |
The full path and filename of the file to be opened. |
nMode |
0 or omitted |
Read-only, buffered. |
1 |
Write-only, buffered. |
|
2 |
Read/write, buffered. |
|
10 |
Read-only, unbuffered. |
|
11 |
Write-only, unbuffered. |
|
12 |
Read/write, unbuffered. |
|
nHandle |
–1 |
An error has occurred; see FERROR() for more information. |
Positive integer |
File successfully opened. |
nHandle = FOPEN("C:\AUTOEXEC.BAT", 0)
The above example opens the AUTOEXEC.BAT files to be read, with buffering turned on. Buffering is normally the preferred method of access.
FPUTS()
FPUTS()
writes data to an opened low-level file. FPUTS writes characters until it reaches the smaller of these two limits: the number specified in the third parameter, or the total length of the string supplied.
nRetVal = FPUTS( nFileHandle, cString [, nBytes ] )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
cString |
Character |
Character string to be written to file. |
nBytes |
Numeric |
Maximum number of bytes to write; defaults to the length of the string supplied plus an ending carriage return-line feed pair. |
nRetVal |
0 |
An error occurred. Test with FERROR(), check for correct file handle, file rights. |
Integer |
The number of bytes written. |
* Write characters to file handle 8,
* starting at the current file position,
* ending at the end of the string or
* the specified number of bytes
lnRetVal = FPUTS(8,MyTestStr,127)
The return, lnRetVal, reflects the number of characters written, including a final carriage return (CHR(13)) and line feed (CHR(10)).
FREAD()
FREAD()
reads data from a low-level file. Unlike FGETS()
, FREAD()
just reads the number of characters specified, ignoring the value of those characters. (FGETS()
is sensitive to carriage returns and line feed characters.) FREAD()
stops after reading the number specified in the second parameter, or encountering the end-of-file.
cString = FREAD( nFileHandle, nBytes )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
nBytes |
Numeric |
Maximum number of bytes to read. |
cString |
Character string |
Characters read. |
* Read characters from file handle 8,
* starting at the current file position,
* ending at the specified number of bytes, or the end-of-file
*(hint: test FEOF())
lcRetVal = FREAD(8,400)
FSEEK()
FSEEK()
repositions the file pointer within a low-level file. This is sort of the low-level file function equivalent of the GO command for DBFs.
nPosition = FSEEK( nFileHandle, nLocation [, nRelativeTo ] )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
nLocation |
Numeric |
Location at which to position the file pointer. |
nRelativeTo |
0 or omitted |
Measure nLocation from the beginning of the file. |
1 |
Measure nLocation from the current file pointer position. |
|
2 |
Measure nLocation from the end of the file; that is, start at the end and move nLocation characters toward the beginning. |
|
nPosition |
Integer |
The new position within the file, expressed as the number of bytes offset from the beginning. |
* Moves the file pointer to a position 400 characters
* from the beginning of the file
lcRetVal = FSEEK(8,400)
=FSEEK(8,0,0) && GO TOP
=FSEEK(8,0,2) && GO BOTTOM
lnWhereAmI = FSEEK(8,0,1) && return current file position
FWRITE()
FWRITE()
writes data to a low-level file. Unlike FPUTS()
, FWRITE()
writes exactly what you specify, without appending any characters (such as carriage returns or line feeds). Use FWRITE()
when you are trying to write data out to a very specific file format, such as Windows CardFile, FOXPRO.INT or another binary format.
nRetVal = FWRITE( nFileHandle, cString [, nBytes ] )
Parameter |
Value |
Meaning |
nFileHandle |
Integer |
The file handle created with FOPEN() or FCREATE(). |
cString |
Character |
The data to be written to the file. |
numBytes |
Numeric |
Maximum number of bytes to write. |
nRetVal |
0 |
An error has occurred. Check for correct file handle, string, disk space and rights. |
Integer |
The number of characters actually written. |
nRetVal = FWRITE(8,REPLICATE("Fred",200),400)
The above example writes 400 characters to file handle 8, starting at the current file position.
Append Memo, Close All, FileToStr(), MemLines(), MLine(), Set Alternate, Set TextMerge, StrToFile()