Logo

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.

Set, SET(), SYS(2001)

Probably the single most destructive command in the language, the SET command gives Visual FoxPro much of its power and much of its flakiness.

Usage

SET SettingName ON | OFF
SET SettingName TO uExpression

SET is a tremendously powerful command to change the behavior of FoxPro, while at the same time it can be a frustrating tool to work with. Don’t think so? Add one line to your startup program—SET EXACT ON—and watch your system fall to pieces. Computer language purists object to SET commands for a pretty straightforward reason: the formation of the infinite-state machine. Although that sounds cool, it isn’t the machine you want to try to make a living on. A finite-state machine may be in a fixed number of predictable states, and therefore results may always be predicted from the current state. Now, however, introduce a single SET command, say, EXACT. Now there are two states for all the code within your system—one set of behaviors for EXACT ON and one for EXACT OFF. That’s easy enough—you can code for this eventuality, by testing SET(“EXACT”) before any test that depends on it. But add a second SET command, say, TALK, and you now have four states to test. Another SET command, such as DATE, introduces 11 different states all by itself, and in combination with SET CENTURY and SET MARK TO, a dazzling assortment may be formed.

So should we avoid SET commands altogether? No, we just need to use them carefully. Along with bringing confusion to the possible states in which Visual FoxPro can find itself, the SET command gives Visual FoxPro the flexibility to globally configure itself to behave in a number of rational ways. Unfortunately, these “rational ways” can often be rent asunder by one bad bit of coding that leaves the application in an unusual state. A subroutine you mug off CompuServe that inadvertently leaves SET EXACT ON will suddenly make your most rock-solid code roll over on its back, eyes bulging. Perform a SEEK on “A,” hoping to find the first record beginning with “A,” and nothing comes back. SETs are definitely two-edged swords.

However, the use of SET commands can simplify application development. For example, rather than continuously having to check the proper display of a currency figure, one set of SET CURRENCY, SET SEPARATOR, SET POINT commands can cause all (or almost all) FoxPro commands to behave appropriately without further intervention on your part. So they are useful while at the same time infuriating.

SET commands are global in scope. Before Visual FoxPro, this was absolute—one SET command affected all following code—but now some SET commands have been limited to affecting only a single data session, but everything within that session is affected. These “semi-global” commands are:

ANSI
AUTOSAVE
BLOCKSIZE
CARRY
CENTURY
COLLATE
CONFIRM
CURRENCY

DATABASE
DATE
DECIMALS
DELETED
DELIMITERS
EXACT
EXCLUSIVE
FIELDS
FIXED

HOURS
LOCK
MARK TO
MEMOWIDTH
MULTILOCKS
NEAR
NULL
POINT

REFRESH
REPROCESS
SAFETY
SET SECONDS
SEPARATOR
SYSFORMATS
TALK
UNIQUE

SET commands range from SET ALTERNATE to SET XCMDFILE. We won’t cover all of them here, but we’ll point out some generalities. To every rule there is an exception, and SET commands prove this time and time again.

In general, there are two ways you’d want to use a SET command: globally, so that it takes effect, and you can assume that condition, throughout your application; and locally, in order to take advantage of a feature only available within one SET mode. Let’s talk about each of these needs and strategies for achieving them.

Global settings are settings you would probably like to establish once and be done with it. FoxPro offers three ways for you to accomplish this. First, many settings are available through the Tools-Options dialog and are stored in the Windows Registry (for more details, see “Registration Database”). Second, all settings stored within the CONFIG.FPW file are read on setup and are in effect for the entire session, unless reset, or unless you’ve accidentally specified the wrong configuration file (check “Configuration Files” for more details on this). Finally, you can set up a program to explicitly issue the appropriate SET commands. Depending on your specific needs, each of these methods may be best for you.

Using the Registry is great. It’s consistent with other Windows applications. The Registry functions and structure allow you to set up more than one user on the same hardware, each with her own settings. The latest versions of Windows 95 and NT provide the ability for administrators to manage Registry entries across the network. However, Registry settings are not read at startup by runtime modules, so you’ll need to resort to alternative methods for runtime environments. However, don’t give up on the Registry altogether. Even if you decide to programmatically load your settings, you might want to consider keeping these settings in the Registry.

In many cases, it can be simpler to embed a CONFIG.FPW file into your .EXE, especially for settings you will not allow users to change. Add the CONFIG.FPW (this must be its name) to the Project Manager as a file, and the settings are read on startup. If you do want or need the users to change settings in the CONFIG.FPW, you can include the file by any name on the disk and point to it with the -C startup switch—see “CONFIG.FPW” for details.

Finally, the programmatic method offers a lot of flexibility. Store all your settings in a table, perhaps stored centrally for ease of maintenance and to allow your users to move from machine to machine, and read each of the settings and process them. You might consider this method with the Registry as your centralized database—if you’re using Windows 95 and/or Microsoft’s System Management Server. Local settings, on the other hand, are those you need in effect for one small set of commands to ensure they work correctly. The safest, though not necessarily bulletproof, method of manipulating SET commands within a “black box” function is to preserve the initial value, change the setting to the desired one, perform the SET-dependent function, and then immediately restore the setting, as in the following code snippet:

cDateSet = SET("DATE")
SET DATE ANSI
sMyDate = CTOD(cYear+"."+cMonth+"."+cDay)
SET DATE &cDateSet

The local setting method above does have its hazards. If any function or command can interrupt the execution of this snippet, you’re back into the infinite-state machine—who knows what will happen? Two situations where we know this may happen are the execution of ON KEY LABEL functions and the automatic refreshing of a Grid/Browse. As usual, a little defensive programming can minimize the hazards. Within code called by ON KEY LABEL, use the same technique as above to save-change-restore any settings you need to work. Grids/Browses are a little trickier—the danger occurs if you change a setting that the grid depends upon for a display function, such as the SET DATE function above. The only work-around we know of is to use naming conventions in such a way that you’ll know when it is unwise to make such changes. An alternative is not to use specific SET commands, such as SET DATE, within your routines if you plan to use grids—instead, use CASE statements to determine the current environment and use those settings to properly process your routine. Take a look at the examples included with the BITx() functions for some ideas on how to do this.

Usage

uRetVal = SET( cSetting [, 1 | 2 | 3 ] )
uRetVal = SYS( 2001, cSetting [, 1 | 2 | 3 ] )

The SET() and identical SYS(2001) functions return the values of a setting. All but a few—(SET(“MOUSE”,1), SET(“HOURS”) and SET(“DECIMALS”), for example) return character strings. A numeric argument of 1 or 2 returns additional information in some cases, and 3 works for SET(“PRINTER”). In those cases where there are only two settings to return—one with and one without the parameter—you can substitute almost anything for the second parameter and get the same result. “FRED,” $123.45, DATE(), .T., and .NULL. all seem to work for us. A few SET commands, such as SET FUNCTION TO, cannot be detected with a matching SET() function.

Many of the SET commands are overloaded. SET FIELDS, for example, can be ON or OFF, or it can be a field list, or it can be LOCAL or GLOBAL. So how do you determine which of these values is returned by the SET() command? –Easy—overload that function as well. SET(“FIELDS”) returns “ON” or “OFF,” SET(“FIELDS”,1) returns the list of fields upon which commands such as EDIT and BROWSE work, and SET(“FIELDS”,2) returns either GLOBAL (fields can be in more than one table and can contain calculated fields) or LOCAL (only fields from the current table can be used). Whew! Got all that?

The options for each SET() function are described with that SET command.

Example

cDelSet = SET("DELETED")

See Also

Display Status, Configuration Files, Registration Database