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.
It’s not surprising that a language with as many different roots as FoxPro has a number of odd behaviors—things that make you say “hmmm.” Some of them, of course, are bugs. But this section is dedicated to those that really aren’t bugs—the ones for which there’s a legitimate explanation. You’ll also find a number of these items in the Reference section. Often, you’ll find a design icon next to them.
Comments and continued lines can get you in trouble. On the whole, the trouble isn’t as severe as it used to be (before VFP 5), but you can still get yourself in trouble.
The problem comes up when you put the continuation “;” after a comment. The next line is still considered part of the comment, even if it doesn’t start with a “*”. So, the following:
* This is the comment before the command ;
SELECT Something FROM SomeWhere INTO SomeOne
is actually treated as a comment. The query never executes.
Personally we like to put a “*” at the beginning of each line of a comment, but there is a situation in which we find the above behavior pretty handy. Sometimes, we need to comment out a command for testing purposes or because we’ve replaced it with a new version. If the command is continued onto multiple lines, it’s sufficient to just stick a “*” in front of the first line. Of course, starting in VFP 5, it’s easy enough to highlight, right-click, and choose Comment, too.
In VFP 3 and FoxPro 2.x, there’s another situation where continuation can get you in trouble. You can’t put in-line comments on a continued line. That is, lines like:
SELECT Something ; && here's the fields
FROM SomeWhere ; && and the table
INTO SomeOne && put it here
fail because FoxPro just drops the “;” and concatenates the whole thing into a single line. This behavior was changed in VFP 5 to our great relief.
When people started running existing applications in VFP 5, lots of them started seeing syntax errors in code that had been running for years. The code hadn’t changed, so what was going on?
Way back in Xbase history days, someone decided that you should be able to put comments on the same line as the structured programming commands without having to use the comment indicator. That is, you could write:
IF x<3 Fewer than three remain
...
ENDIF
The rule applied to each of the components of the branching
and looping commands (IF
, DO CASE
, DO WHILE
, FOR
and SCAN
). In fact, we have
little doubt it was really set up this way so you could write stuff like the
following (by the way, the dots used to be required around the Boolean logical
operators .AND.
, .NOT.
and .OR.
):
DO WHILE .NOT. EOF()
* process records
ENDDO WHILE .NOT. EOF()
It looks real nice, but it turned out to cause a rather nasty, subtle problem. When FoxPro parsed one of these lines, it stopped as soon as it reached something syntactically incorrect. That’s right, as soon as the parser found a syntax error, it figured it had a comment and gave up. Consider, for example:
IF x<3 .AND y>7
As far as versions of FoxPro through VFP 3 are concerned, that line only checks whether x is less than 3. The second part of the condition was totally ignored.
VFP 5 changed the rules. You’re still allowed to include
comments without an indicator on lines that don’t have any executable code
(like ENDDO
, ENDIF
, DO CASE
and so on), but if a line contains code (like IF
or
DO WHILE
), the whole line is checked. Since we think the original design
decision was terrible, we’re delighted by the change. Anything to help root out
stubborn hidden bugs is welcomed.
By the way, the parser got stricter in other ways as well.
Used to be that extra right parenthesis at the end of an expression were
ignored. No more. Starting in VFP 7, spaces are no longer allowed to separate
variables in LOCAL
, PUBLIC
, PRIVATE
, LPARAMETERS
, or PARAMETERS
statements (for
example, LOCAL MyVar1 MyVar2
is now a no-no). In general, from VFP
5 forward, the compiler does a better job of finding syntax errors and ensuring
that you’re running the code you think you’re running.
On the whole, the designers of Visual FoxPro have done a tremendous job marrying object-orientation to Xbase. But there are places where the marriage seems a little rocky. The use of single-letter identifiers is one of them.
Traditionally, the letters A through J are alternate names for the first 10 work areas. (When the number of work areas went up to 25, Fox Software didn’t extend this convention. It’s just as well—they’d have a heckuva time finding 32,767 different characters to represent the work areas in Visual FoxPro.) In addition, the letter M was reserved to indicate that what followed was a memory variable.
So, how would this cause you any trouble? Well, what if you
have a table named C.DBF? When you open it with USE C, you’d expect it to have
the alias of “C”, right? Only if you’re really lucky. Because
“C” is reserved for the third work area, the only way C.DBF will have
an alias of “C” is if you open it in the third work area. Otherwise,
it’ll have an alias of the work area it’s opened in (“A” through
“J” if it’s opened in one of the first 10 work areas, or
“W” followed by the work area number if not). So, when you use code
like SELECT C
to select the table, you’ll really be selecting the third work
area, which may have an entirely different table open or no table open at all.
OOP makes it even worse. Object-orientation uses the same
type of “dot” notation that fields in tables do to spell out the
complete name of an object, like frmMyForm.grdMainGrid.colName.txtName
. So
what’s the problem? There is none, unless you try to use one of the letters A-J
or M for the name of an object. Code like the following:
a=CreateObject("form")
a.Caption="My Form"
is doomed to failure. You can create the form, but the assignment blows up on you as VFP goes looking for a field named Caption in the first work area.
Of course, single-letter variable and table names are a lousy idea anyway (most of the time—we’re still fond of variables like x, y, z, and o for quick and dirty testing), so the workaround for this isn’t terribly painful. It’s like the old joke—“Doctor, it hurts when I do this.”—“Then don’t do that.” Use names longer than one character and you’ll never run into this problem.
Here’s a behavior that drove people nuts in FoxPro 2.x and, even though the whole context surrounding it has changed in Visual FoxPro, it continues to drive people nuts.
Say your user is entering a new record and is sitting on a field that requires validation. After entering bad data for that field, but before moving focus to another field, the user clicks the Save button on your toolbar or chooses Save from the menu. Whoosh—the data is saved, including the bad data. What happened?
In VFP, as in FoxPro 2.x, the Valid routine for a text field doesn’t execute until focus leaves that field. Clicking a button on a toolbar or picking a menu item doesn’t change focus and therefore doesn’t fire the Valid method or the LostFocus method, of course. (In fact, a toolbar never has focus, which lets you do pretty cool things.)
Why does it behave this way? Because we want it to. It feels
wrong in this situation, but suppose the toolbar or menu item the user chose
was Select All or Paste. We sure wouldn’t want the focus to change (and the
Valid
and LostFocus
methods to fire) in that case.
So how do we make sure the data gets validated? Simple—make sure focus changes. One way to do this is to reset focus to the same field:
_SCREEN.ActiveForm.ActiveControl.SetFocus()
Since menus and toolbars don’t get the focus,
_SCREEN.ActiveForm
is still the same form as before. We just set the focus back
to the object that had it, which triggers that control’s Valid
, LostFocus
, When
and GotFocus
methods.
Letting users choose the printer for a report at runtime and having it automatically adjust itself for that printer’s settings ought to be a piece of cake. Isn’t that one of the things Windows is supposed to handle for us? Unfortunately, VFP tries to be too smart and ends up making things a lot harder than they need to be.
When you create a report with the Report Designer, information about the currently selected printer and its settings is stored in the first record of the report table (FRX). When you print the report, VFP checks that information and uses it. If the selected printer was the Windows default, VFP is smart enough to use whatever printer the user chose. But if the selected printer was something else, VFP assumes that you meant it when you created the report with that printer chosen, and it expects to print to that printer.
Why does it behave this way? Well, we think the developers were trying to be helpful. They figured that, if you’d gone to the trouble of configuring a printer especially for the report, they ought to pay attention. The problem with this is that the people using your application probably don’t even have the same printer you do. The other problem is that this is such a non-intuitive way of arranging for special settings that we can’t imagine anyone doing it on purpose.
So how do you get VFP to print to the user’s chosen printer and honor that printer’s settings? Easy—throw out the stored settings. The information is in the Tag, Tag2 and Expr fields of the first record of the report. Just blank ‘em out.
Our friend Brad Schulz, who knows more about printing from FoxPro than anyone else, suggests making a copy of the report at runtime, blanking the Tag, Tag2 and Expr fields of the copy, then stuffing the Expr field (which is plain text in an INI-file type format) with the settings you really want. Then, use the copy to run the report. Another friend, Rick Schummer, is a wiz at application deployment. He uses a ProjectHook for his projects that, when an EXE is built, automatically opens every report in the project and blanks the necessary fields. That way, he doesn’t have to worry about forgetting this important step when he’s preparing to send the application to his client.
Normally, the folks at Microsoft (and, before them, the folks at Fox Software) go out of their way to make sure that, whatever they change in a new version of FoxPro, code that ran in the old version will still run in the new one. But in VFP 6, they broke that rule. Not only that, they did it on purpose. Say what?!
Because the year 2000 was looming, the Fox team figured it
was about time to really make FoxPro developers aware of the bugs lurking in
their old code. So they added the SET STRICTDATE
command to help us find
problems and to keep us from making them in the first place. When you turn
STRICTDATE
on, VFP lets you know if any of your code contains ambiguous dates.
Great, that sounds good.
But here’s the catch. By default, it’s set to moderate
strictness in the development environment. That means that any date or datetime
constants that don’t use the long unambiguous format (that is, {^1958-09-01}
rather than {9/1/58}, the latter of which could be either January 9 or
September 1, depending on what SET DATE
is used) cause an error. (See the
Reference section for the details.)
Well, it’s nice that they can find our errors for us, but why they heck didn’t they make the old way the default? Because if they had, we’d keep going along in our misguided belief that our old code was Y2K-compliant. They’re forcing us to pay attention.
About now, you’re probably wondering how you’re supposed to
upgrade your users to newer versions of VFP if all your dates are going to
fail. That’s easy. By default, STRICTDATE
is set to 0 for them (that is, at
runtime), but you’ll want to set it to 2 on your development machine so you can
catch and squash these bugs.
As with VFP 6, some things were changed in VFP 7 that might break code written in earlier versions (although that’s much less likely than the changes made in VFP 6).
A couple of changes were made to the _DBLCLICK
system
variable. First, it no longer contains both the interval necessary for two
clicks to be considered a double-click and the interval during which
keystrokes will perform incremental searching (which seems like a daft
combination in hindsight). Starting in VFP 7, the latter interval is contained
in the _INCSEEK
system variable. Second, rather than the default of 0.5 seconds
that previous versions had, _DBLCLICK
now defaults to the value set by the user
in the Mouse Control Panel applet. That means we can now create applications
that are more respectful of the user’s wishes (what a concept!). Thus, there’s
very little reason for you to touch _DBLCLICK
in your applications anymore. For
lack of a similar system setting for incremental searching, _INCSEEK
also
defaults to the user’s specified double-click setting. We recommend you add
this one to the Options dialog of your applications and let the user set it
there.
Another change is that the Top
, Left
, Height
and Width
properties of _VFP
and _SCREEN
are no longer identical. The properties of _VFP
now reflect the entire VFP window, while those of _SCREEN
contain the values of
the “client” area of the window, the white area of the window that
doesn’t include the title bar, menu bar, toolbars, status bar, and so forth.
This is a Good Thing, since we can now determine how much actual space we have
to work in without having to adjust for the size of the title bar, menu bar,
toolbars, status bar, and so forth. However, if you have code that subtracts
these sizes from the _SCREEN
properties, you’ll end up with values that are
much too small. Be sure to look for any such code and either use _VFP instead
of _SCREEN
or (a better idea) don’t make any adjustments to the values of the
_SCREEN
properties.
The spelling checker included with earlier versions was dropped in VFP 7. Since you couldn’t legally distribute it with your applications, the only code this should break is developer tools. If it breaks any client code, you need to check REDIST.TXT in the VFP home directory to see what other files you might be distributing in violation of the VFP license agreement!
The CENTRAL
, FOXCODE
, FOXGEN
, FOXGRAPH
and FOXVIEW
commands
were removed in VFP 7. Since these had no place in any application and have
been obsolete for years, that shouldn’t break any code.