foxtalk - dfpug-portalportal.dfpug.de/dfpug/dokumente/foxtalk/pdf2000/ft0400.pdffoxtalk continues on...

39
1 Zipping and Unzipping for Clients Richard David Hodder 2 Editorial: VFP Community to Get First Shot at New Microsoft Certification Exams Whil Hentzen 9 “clBackRest”: An Ergonomic Solution to a Common Pain in the Neck Chad J. Lemmer 13 Reuseable Tools: Manage Your Applications Doug Hennig 17 Beyond the View Designer Jim Booth 19 The Kit Box: Our Department Needs You! Paul Maskens and Andy Kramek 23 April Subscriber Downloads EA The Mere Mortals Framework 6.0 Kelly Conway EA Cool Tool: A New Spin on Thermometer Bars Whil Hentzen EA Third Party Tool Forum: Controlling User Access to Application Data in Foxfire! Bill Wood April 2000 Volume 12, Number 4 Fox Talk Continues on page 3 Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers Accompanying files available online at http://www.pinpub.com/foxtalk Applies specifically to one of these platforms. Applies to FoxPro v2.x Applies to VFP v5.0 Applies to VFP v3.0 Applies to VFP v6.0 (Tahoe) 6.0 6.0 Zipping and Unzipping for Clients Richard David Hodder Compression software is a staple tool on the developer’s belt. The DynaZIP-AX ActiveX controls from Inner Media, Inc. give developers programmatic control over creating and reading ZIP files. In this article, Richard David Hodder introduces you to the capabilities of these controls. Next month, he’ll dig deeper into the controls and provide a small framework for their use. P KZIP and PKUNZIP by PKWARE, Inc. have become household names in the software industry. These are DOS command-line products, and using them to create and manipulate ZIP files in VFP is fraught with difficulty. First of all, you must use the RUN command to shell out to DOS to run the programs. The DOS window appears on the toolbar. There are memory issues and PIF settings that might need to be tweaked on a user-by-user basis. Occasionally, DOS windows will remain onscreen after a DOS executable finishes. And sometimes it’s necessary to put in calls to INKEY() to wait for the DOS program to finish. Second, to find out the success of the attempt to zip or unzip, the log file must be parsed to determine whether everything was successful. Third, particularly when zipping or unzipping a large amount of information, it’s important to give the user visual feedback as to the status of the zipping/unzipping. Windows users expect (dare I say demand?) something akin to a thermometer during waiting periods. There’s no way I know of to hook into PKZIP/PKUNZIP for status information, so unless your clients enjoying watching the DOS box, they’ll have to wait until the process is finished. This leaves the door open for inquisitive users to click on the DOS box and cause havoc! To paraphrase the Warner Brothers cartoon representation of John Steinbeck’s Of Mice and Men, “I’m gonna click on the pretty DOS window, George, and then I’m gonna hug it and squeeze it.…” 6.0 6.0

Upload: others

Post on 05-Apr-2020

9 views

Category:

Documents


1 download

TRANSCRIPT

1 Zipping and Unzippingfor ClientsRichard David Hodder

2 Editorial: VFP Community toGet First Shot at New MicrosoftCertification ExamsWhil Hentzen

9 “clBackRest”: An ErgonomicSolution to a Common Painin the NeckChad J. Lemmer

13 Reuseable Tools: ManageYour ApplicationsDoug Hennig

17 Beyond the View DesignerJim Booth

19 The Kit Box: Our DepartmentNeeds You!Paul Maskens and Andy Kramek

23 April Subscriber Downloads

EA The Mere MortalsFramework 6.0Kelly Conway

EA Cool Tool: A New Spin onThermometer BarsWhil Hentzen

EA Third Party Tool Forum:Controlling User Access toApplication Data in Foxfire!Bill Wood

April 2000Volume 12, Number 4

FoxTalk

Continues on page 3

Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers

Accompanying files available onlineat http://www.pinpub.com/foxtalk

Applies specifically to one of these platforms.

Applies toFoxPro v2.x

Applies toVFP v5.0

Applies toVFP v3.0

Applies to VFPv6.0 (Tahoe)

6.06.0

Zipping andUnzipping for ClientsRichard David Hodder

Compression software is a staple tool on the developer’s belt. The DynaZIP-AXActiveX controls from Inner Media, Inc. give developers programmatic control overcreating and reading ZIP files. In this article, Richard David Hodder introduces you tothe capabilities of these controls. Next month, he’ll dig deeper into the controls andprovide a small framework for their use.

PKZIP and PKUNZIP by PKWARE, Inc. have become household names inthe software industry. These are DOS command-line products, andusing them to create and manipulate ZIP files in VFP is fraught

with difficulty.First of all, you must use the RUN command to shell out to DOS to run the

programs. The DOS window appears on the toolbar. There are memory issuesand PIF settings that might need to be tweaked on a user-by-user basis.Occasionally, DOS windows will remain onscreen after a DOS executablefinishes. And sometimes it’s necessary to put in calls to INKEY() to wait for theDOS program to finish.

Second, to find out the success of the attempt to zip or unzip, the log filemust be parsed to determine whether everything was successful.

Third, particularly when zipping or unzipping a large amount ofinformation, it’s important to give the user visual feedback as to the status ofthe zipping/unzipping. Windows users expect (dare I say demand?)something akin to a thermometer during waiting periods. There’s no way Iknow of to hook into PKZIP/PKUNZIP for status information, so unless yourclients enjoying watching the DOS box, they’ll have to wait until the process isfinished. This leaves the door open for inquisitive users to click on the DOSbox and cause havoc! To paraphrase the Warner Brothers cartoonrepresentation of John Steinbeck’s Of Mice and Men, “I’m gonna click on thepretty DOS window, George, and then I’m gonna hug it and squeeze it.…”

6.06.0

2 http://www.pinpub.comFoxTalk April 2000

From the Editor FoxTalk

VFP Community to Get FirstShot at New MicrosoftCertification ExamsWhil Hentzen

IF you’ve already taken the VFP 6.0 certification exam,you probably thought it was safe to forget where thelocal test-taking facility was located, didn’t you? Well,

hot on the heels of the VFP exams comes another set ofexams, and this time, Visual FoxPro developers get to bethe first to try them out.

Many of you are aware that Visual FoxPro doesn’tbehave like other Microsoft software—no wisecracksabout it actually being fast, not having bugs, that sort ofthing. Rather, what I’m talking about is the way thatVFP’s built on the inside, and how it communicates withWindows. For example, if you have a form in Visual Basiccontaining four command buttons, that form is using fiveWindows resources—one Windows handle for the form,and four more Windows handles—one for each of thecommand buttons.

This behavior is both good and bad—good in thatVisual FoxPro can be much more efficient in manysituations than comparable tools, and bad in that youcan’t assume a VFP app is going to act appropriately, asyou’d expect it to in the Windows environment. Perhapsthe easiest of these behaviors to demonstrate is the use ofa sophisticated screen capture program that allows you tocrop a screen shot according to the Active Window. Inother applications, doing so will capture what you and Iwould call the “ActiveForm,” but in VFP, you’re relegatedto grabbing the entire application, then manuallycropping to grab just the form you’re interested in.

This behavior, of course, has its roots in the days ofFox Software back in Toledo, when Dr. Dave and cohortswanted everything to be fast-fast-fast, and, as a result,played a lot of tricks with hardware and memory tooptimize performance. How many of you remember thecryptic DOS message

bnormal Program Termination

that appeared out of nowhere in the FoxPro 1.0 and 2.0days? Yes, Fox liked memory, lots of it, and, once in awhile, it didn’t care if it tried to grab the memory thatanother program happened to be using at the time.

Anyway, I digress. The point is that VFP stresses thehardware like few other applications, other than games,do. And this is why the VFP community has first dibs onthe new set of certification exams Microsoft is releasingthis year. And in another FoxTalk exclusive, I’ve beenasked to make the only public announcement aboutthese exams.

So what are these exams? You’re all familiar with thevarious certification tracks: MCSD, the one for MicrosoftCertified Solution Developer (that’s French for“programmer”); MCDBA, Microsoft Certified DatabaseAdministrator; MCT, Microsoft Certified Trainer; MCP,Microsoft Certified Professional; MCSE, MicrosoftCertified Systems Engineer; and the all-new MCSOO,Microsoft Certified Something-Or-Other.

So who has this slew of exams and programs left out?The end user, that’s who! Microsoft has now begun theprocess of certifying end users, and naturally, the firstplace that’s going to be certified is where the rubber meetsthe road, or, more accurately, where the user meets thecomputer—at the mouse, the keyboard, and the monitor.

In order to gain Microsoft Accelerated CertifiedHardware End Users certification, or MACHEU (yes, it’spronounced “mmmm-a-CHOO!” just like you thought itwould be), an individual will have to pass three tests—one each for the monitor, the keyboard, and the mouse.Naturally, because there are so many types of thesedevices, a user will in some cases have his or her choice ofwhich test to take, just as VFP developers had their choiceof the Desktop or Distributed exams.

New exams“Exam 69-101: Installing, Powering Up, and ConfiguringCRT Monitors” will test an end user’s basic ability to usea standard cathode-ray-tube monitor. The user will beexpected to identify the front, sides, and back of amonitor; identify whether or not the monitor has a hiddencontrol panel; locate and open the control panel ifappropriate; and manipulate each of the controls,including horizontal and vertical resize, horizontal and

Continues on page 23

http://www.pinpub.com 3FoxTalk April 2000

Finally, there’s a price, minimal though it is. PKZIPand PKUNZIP must be on each user’s machine, andthere’s no royalty-free distribution of PKZIP andPKUNZIP—it’s necessary to purchase a copy of PKZIP foreach user.

DynaZIPInner Media, Inc. has created a product called DynaZIP,which includes (among other things) ActiveX controls thatprovide developers with programmatic control overzipping and unzipping ZIP files.

Forms of productDynaZIP is packaged in three different ways:

1. DynaZIP Complete 162. DynaZIP Complete 323. DynaZIP-AX

The “complete” products include ActiveX controls,Static DLLs, OCXs, and VCLs. The AX product has onlythe 32-bit ActiveX controls. This article focuses on theDynaZIP-AX product.

Evaluation copiesSome vendors offer what are called “crippled” versions ofproducts for evaluation. “Crippling” refers to the fact thatyou can’t test all of the functionality of the product. Inaddition to crippling, vendors often put what are calledevaluation “time bombs” into the demo. These timebombs aren’t destructive; they merely prevent you fromusing the demo product after an evaluation period haselapsed. Both of these techniques are used to generateinterest in a product without giving away the farm.

Inner Media, Inc. provides free evaluation copies ofits software on its Web site (http://www.innermedia.com/html/free_demos.htm). The evaluation copies aren’tcrippled and have no time bombs in them. The evaluationperiod for the product is 30 days, but you’re on the honorsystem. To quote the company’s Web site, “Of course, weare relying on your honor to treat these copyrightedworks properly, and to come back within 30 days andpurchase a legal copy once you’ve decided it’s right foryou. You trust us with your precious data, and we’ll trustyou to honor our license agreement.” The evaluation copyeven includes a huge manual in PDF format and codesamples in several different development languages.Inner Media goes a step further, providing 30 days oftechnical support via e-mail. This company wantsyour business!

FeaturesDynaZIP-AX is chock full of wonderful functionality:

• Zipping capability: Create new ZIP files, add ordelete items from existing ZIP files, and freshen orupdate existing ZIP files. ZIP from memory to an itemin a ZIP file, or ZIP from memory to memory.

• Unzipping capability: Extract all or just some itemsfrom a ZIP file; get information about each item in aZIP file. Extract all or a portion of a ZIP file itemto memory.

• Long filename support: Automatically supports longfilenames in the Windows 95/98 and Windows NTenvironments. Includes options for zipping andunzipping to systems that don’t supportlong filenames.

• UNC support: You can use the Universal NamingConvention (UNC) instead of the drive letter whenspecifying a path or filename.

• True multi-threading support: Contains truemulti-processed and multi-threaded capableDLLs/components.

• Active Server Pages (ASP) support: Contains samplecode showing how to use the windowless ActiveXCOM components with ASP.

• Renaming capability: Rename items/files duringzipping/unzipping operations.

• Multiple volume support (disk-spanning): CreateZIP files that span more than one disk.

• Encryption/decryption: Add security to your ZIP filesby adding password protection to some or all ofthe items.

• International support: Easily customize/translate allDynaZIP messages that your end user might see.

• Support for many development languages: Choosewhich DynaZIP programming interface to use withyour development environment: ActiveX, OCX, DLL,or VCL. Or use the “DZ_EASY” interface, whichmakes most DynaZIP functions easily available todatabase languages. You can use DynaZIP with manydifferent programming languages, including VisualBasic, VBScript, Delphi, C/C++, Visual FoxPro,Access, PowerBuilder, Pascal, and othercontemporary development environments.

• Double-byte character set (DBCS) Support: Developapplications that service most international markets.With DBCS, your programs will easily managefilenames, messages, and so on. in the full range ofdisplayable characters, long or short.

• Royalty-free distribution: Distribute DynaZIPActiveX DLLs with your applications at no extra cost.

• Diagnostic tools and sample code: Use any of themany useful sample applications and copy parts oftheir source code into your own application.

• Useful documentation: Includes a full manual in PDFformat and online Help.

There are three controls included in DynaZIP-AX: theDynaStat control, the Zip control, and the Unzip control.

Zipping and Unzipping . . .Continued from page 1

4 http://www.pinpub.comFoxTalk April 2000

DynaStat controlThe DynaStat control is an ActiveX thermometer that canbe dropped on a form and used to display status. Figure 1shows what the DynaStat control looks like in action. It’s afairly simple control with just a few important properties:

• StatusPercent (Numeric): This property determinesthe percentage displayed on the thermometer, as wellas how much of the thermometer bar will be filled in.It should contain a value in the range of 0 to 100. InFigure 1, the StatusPercent is set to 10.

• StatusText (Character): This property determines thetext to be displayed in the thermometer area of thecontrol. For example, you might display the name ofthe item currently being compressed. In Figure 1, theStatusText is set to “XYZ.TXT.”

• MarkerColor (Numeric): This property determinesthe color used in the thermometer bar.

• Use3Dflag (Logical): This property determineswhether the control looks three-dimensional or flat.

You can use the DynaStat control in combination withthe MajorStatus and MinorStatus callback events topresent a different status thermometer while zippingand unzipping.

The blender metaphorI recently bought a blender. It doesn’t have an On/Offswitch. It has an Off switch and several blend settings.You want to know the first crucial lesson I learned? Neveradd things to a blender when it’s working! Turn it off first,then add what you want, and then choose your blendsetting. This metaphor applies to both the Zip and theUnzip controls.

Neither the Zip control nor the Unzip control has amethod that kicks off the zipping/unzipping process.Instead, both have a property called ActionDZ that’s set toone action out of a list of possibilities (ZIP_ADD,UNZIP_EXTRACT, or similar actions)—the blend settings,if you will. There are two actions named ZIP_NOACTIONand UNZIP_NOACTION. These settings don’t necessarilyrepresent the social life of developers. They’re DynaZIP’sequivalent of the “Off” switch. It’s a good idea to turn offthe controls using the NOACTION actions betweenchanges in actions.

Zip controlRather than present the entire interface of the Zip control(which is laid out in the documentation), I’ll presentprogressive examples that demonstrate the use of thecontrol, introducing properties as I go.

When I create a ZIP file (with or without DynaZIP), Ineed to know a few basic things. The first thing is the

name of the ZIP file to create. Second is the list of items toadd to the ZIP file. Finally, I need to know how to makethe Zip control add the specified items to the ZIP file (inother words, how to start the blender). The followingcode sample demonstrates how to create a ZIP file, calledexample1.zip, that will contain all files beginning with“File” and ending with a TXT extension in thecurrent directory:

*-- Create the Zip controlloZip = CREATEOBJECT("dzactxctrl.dzactxctrl.1")

WITH loZip *-- Turn off the blender .ActionDZ = ZIP_NOACTION

*-- Set the name of the ZIP file .ZipFile=SYS(5)+CURDIR()+"example1.zip"

*-- Set the items to put into the ZIP file .ItemList = SYS(5)+CURDIR()+"file*.txt"

*-- Tell the Zip control not to store the *-- path of the items in the ZIP file .NoDirectoryNamesFlag = .T.

*-- Tell the Zip control to add .ActionDZ= ZIP_ADD

*-- Check the success of the zip llSuccess = (.ErrorCode = ZE_OK)ENDWITH

Very simple! No messy parsing or DOS windows, andI even get an indication of whether there were any

Figure 1. TheDynaStat control.

http://www.pinpub.com 5FoxTalk April 2000

problems! Let’s cover the properties introduced inthis sample:

• ZipFile (Character): The name basically says it all:This property should be set to the name of the ZIP fileto create or manipulate. When setting this property,it’s very important to include a full path.

• ItemList (Character): This is the filespec identifyingthe files you want to include in the ZIP file. As youcan see from the sample, wildcard characters (forexample, *,?) can be included in the filespec. In fact,multiple filespecs can be included in the ItemList atthe same time, each delimited by a space character.For example, I could set the ItemList property to“c:\rick.* c:\article??.doc c:\readme.txt.” Notice that,like the ZipFile property, a full path must be includedin each filespec entered in this property. WhatDynaZIP does with the items in the ItemList isdetermined by the ActionDZ property (theblender setting).

• NoDirectoryNamesFlag (Logical): This propertydetermines whether the path to the files in theItemList is included in the ZIP file. If this is set to .T.(as in the preceding code), the paths won’t beincluded in the ZIP.

• ErrorCode (Numeric): This property holds a numericcode that represents whether the last actionperformed by the Zip control was successful. Anynon-zero value in this property signals thatsomething didn’t work properly. The Include file forthe Zip control has a list of the error codes (all ofwhich have the prefix “ZE_”) returned in thisproperty.

The ActionDZ property is the blender setting. Itdetermines the specific action to be taken on the items inthe ItemList (among other things). As mentioned before, ithas an “Off” setting (ZIP_NOACTION), and it has sevenother settings. Two of these (ZIP_MEMTOMEM andZIP_MEMTOFILESTREAM) don’t apply to VFP, so theywon’t be discussed here. Four of the actions should bevery familiar to anyone who uses compression software:Add, Delete, Update, and Freshen (the #DEFINEs forthese actions all have a ZIP_ prefix). The ZIP_ADD settingadds the items in the ItemList property to the ZIP file. TheZIP_DELETE setting removes the items in the ItemListfrom the ZIP file; if this setting had been used in thepreceding code, it would have removed file*.txt from theZIP file. When using the ZIP_UPDATE setting, items inthe ItemList that don’t already exist in the ZIP file areadded, and items that already exist in the ZIP and have alater modification date are replaced (updated). TheZIP_FRESHEN setting replaces an existing item in a ZIPfile, but only if it’s been modified more recently than theitem in the ZIP file.

ZIP_MEMTOZIPThe final ActionDZ setting that I’ll discuss isZIP_MEMTOZIP. Two words: “Way cool!” Employing thissetting is like using low-level file functions to add an itemto a ZIP file. Simply fill in the ZipString property with thetext you’d like to be in the file, then set the ActionDZproperty to ZIP_MEMTOZIP. Here’s a sample that createsan entry in the ZIP file called “readme.txt”:

*-- Create the Zip controlloZip = CREATEOBJECT("dzactxctrl.dzactxctrl.1")

WITH loZip *-- Turn off the blender .ActionDZ = ZIP_NOACTION

*-- Set the name of the ZIP file .ZipFile=SYS(5)+CURDIR()+"memtozip.zip"

*-- Set the item in the zip that the *-- memory (stored in ZipString) will *-- be written to .ItemList = "readme.txt"

*-- Set what will get written to readme.txt .ZipString = "Here are the latest release notes..."

.ActionDZ= ZIP_MEMTOFILEENDWITH

This can save a step in a process. Rather than creatinga readme.txt file and then adding it to the ZIP file, I canjust add the readme.txt file directly.

StatusThe Zip control allows the program to present a statusthermometer while zipping. I can set the caption of thethermometer form by setting the ExtProgTitle property.There’s also a numeric property called “ZipSubOptions,”which, among other things, can be used to show a statusthermometer during the action being performed.

*-- Create the Zip controlloZip = CREATEOBJECT("dzactxctrl.dzactxctrl.1")

WITH loZip *-- Turn off the blender .ActionDZ = ZIP_NOACTION

*-- Set the name of the ZIP file .ZipFile=SYS(5)+CURDIR()+"example2.zip"

*-- Set the items to put into the ZIP file .ItemList = SYS(5)+CURDIR()+"file*.txt"

*-- Tell the Zip control not to store the *-- path of the items in the ZIP file .NoDirectoryNamesFlag = .T.

*--Set the thermometer title .ExtProgTitle = "Zipping In Progress"

*-- Use the thermometer included with DynaZIP-AX .ZipSubOptions = ZSO_EXTERNALPROG

*-- Tell the Zip control to add .ActionDZ= ZIP_ADD

*-- Check the success of the zip llSuccess = (.ErrorCode = ZE_OK)ENDWITH

6 http://www.pinpub.comFoxTalk April 2000

A number of #DEFINEs are associated with theZipSubOptions property, and they all begin with theprefix ZSO. The ZipSubOptions property is a set of binaryflags that can be configured by adding together the#DEFINEs for the required functionality. For example, a#DEFINE called ZSO_EXTPROGCANCEL tells theDynaZIP thermometer to surface a Cancel button, andanother called ZSO_RESET_ARCHIVED resets thearchive bit on any file zipped. If I want to use theDynaZIP thermometer with a cancel button and have itreset the archived bit, I’d replace the line beginning with“.ZipSubOptions=” with the following line of code:

.ZipSubOptions = ZSO_EXTERNALPROG + ; ZSO_EXTPROGCANCEL + ; ZSO_RESET_ARCHIVED

The ZipSubOptions property can also be used towrite a log of the zipping activity, ignore long file names,and perform many other functions.

MultiVolume (disk spanning)Disk spanning refers to creating ZIP files that extendacross several diskettes or removable media. The numericproperty MultiVolume configures the type of diskspanning used. If this property is set to zero, no spanningwill occur. A number of #DEFINEs are associated with theMultiVolume property, and they all begin with the prefixMV_. The MultiVolume property is a set of binary flagsthat can be configured by adding together the #DEFINEsfor the required functionality.

loZip.MultiVolume = MV_USEMULTI + ; MV_FORMAT + ; MV_LOWDENSE

The preceding line of code tells the ZIP control to usedisk spanning and to format each disk at low density.

Other properties of interestHere are descriptions of a few more properties. (TheDynaZIP manual covers many others.)

• EncryptCode (Character): Filling in this propertyallows the encryption password (up to 65 characters)to be set for the items being zipped. In addition tofilling in this property, you must set the EncryptFlagproperty to .T. to enable encryption.

• Comment (Character): Filling in this property allowsa user to set the comment of the ZIP file. In additionto filling in this property, the user must set theAddCommentFlag property to .T. to use this functionduring the ZIP action.

• RecurseFlag (Logical): Setting this property to .T. isthe equivalent of telling PKZIP to recurse through thesubdirectories when building the contents of theZIP file.

• FixFlag (Logical): Ever get the message that the ZIPfile is damaged, along with instructions to use PKFIXto attempt to fix it? Setting this flag to .T. will attemptto fix the ZIP file. If the attempt to fix the ZIP file fails,DynaZIP has another flag called “FixHarderFlag” (Ikid you not!) that can be used to try to resurrectyour information.

Unzip controlRather than present the entire interface of the Unzipcontrol (which is laid out in the documentation), I’llpresent progressive examples that demonstrate the use ofthe control, introducing properties as I go.

When I unzip a ZIP file (with or without DynaZIP), Ineed to know a few basic things. The first thing is thename of the ZIP file to extract from. Second is the filespecfor the items to extract from the ZIP file. Third is thedestination—where those extracted files should be put.Finally, I need to know how to make the Unzip controlstart extracting (in other words, how to start the Unzipblender). The following code sample demonstrates how toextract all files beginning with “file” and ending with aTXT extension, moving them from the ZIP file calledexample1.zip into a subdirectory of the current directory,called output:

*-- Create the Unzip controlloUnZip = CREATEOBJECT("duzactxctrl.duzactxctrl.1")

WITH loUnZip *-- Turn off the blender .ActionDZ = UNZIP_NOACTION

*-- Set the name of the ZIP file .ZipFile=SYS(5)+CURDIR()+"example1.zip"

*-- Set which files to extract .FileSpec=”file*.txt”

*-- Set the directory to put the *-- extracted items .Destination = SYS(5)+CURDIR()+"output”

*-- Tell the Zip control to extract .ActionDZ = UNZIP_EXTRACT

*-- Check the success of the unzip llSuccess = (.ErrorCode = UE_OK)ENDWITH

Once again, very easy! Let’s cover the propertiesintroduced in this sample.

• ZipFile (Character): The name says it all: Thisproperty should be set to the name of the ZIP file tomanipulate. When setting this property, it’s veryimportant to include a full path.

• FileSpec (Character): This is the filespec identifyingthe files that you want to extract in the ZIP file. Asyou can see from the sample, wildcard characters (forexample, *,?) can be included. In fact, multiplefilespecs can be included in the FileSpec property atthe same time, each delimited by a space character.

http://www.pinpub.com 7FoxTalk April 2000

For example, I could set the FileSpec property to“rick.* article??.doc.” What DynaZIP does with theitems in FileSpec is determined by the ActionDZproperty (the blender setting).

• Destination (Character): This property determinesthe location (path and directory) to which the items inFileSpec will be extracted. When setting this property,it’s very important to include a full path.

• ErrorCode (Numeric): This property holds a numericcode that indicates whether the last action performedby the Unzip control succeeded. Any non-zero valuein this property signals that something didn’t workright. The Include file for the ZIP control has a list ofthe error codes (all of which have the prefix “UE_”)returned in this property.

Whereas the primary focus of the ActionDZ actions inthe ZIP control is on updating a ZIP file, only two of the11 actions available to the Unzip control pertain toextracting files (UNZIP_EXTRACT and UNZIP_FILETOMEM). As with the ZIP control, there are two actions(UNZIP_MEMTOMEM and UNZIP_MEMTOFILESTREAM) that don’t apply to VFP and thus won’t bediscussed here. A description of the actions appears in thesidebar titled “ActionDZ Settings for Unzip” on page 8.

UNZIP_FILETOMEMMore ”Way cool!” This action is the converse toZIP_MEMTOFILE. This action allows you to read sectionsof an item in a ZIP file, as if you were using low-level filefunctions. The following code reads the first 20 charactersof the readme.txt item in memtozip.ZIP, then displays amessage box showing the extracted text. At no timeduring the extracting of this ZIP file did the readme.txtitem leave the ZIP file:

*-- Create the Unzip controlloUnZip = CREATEOBJECT("duzactxctrl.duzactxctrl.1")

WITH loUnZip *-- Turn off the blender .ActionDZ = UNZIP_NOACTION

*-- Set the name of the ZIP file .ZipFile = SYS(5)+CURDIR()+"memtozip.zip"

*-- Set which files to extract .FileSpec = "readme.txt"

*-- Specify how much text to read *-- from the readme.txt file .UnzipStringSize = 20

*-- Specify the character offset that *-- determines where to start reading *-- the contents of readme.txt. *-- 0 = first character in the file .UnzipStringOffset = 0

*-- Tell the Zip control to extract .ActionDZ = UNZIP_FILETOMEM

*-- Display the string in a message box MESSAGEBOX(.UnZipString)ENDWITH

Assuming that you ran the ZIP_MEMTOFILE codesample, you’ll see a message box that displays the text“Here are the latest.”

StatusDisplaying a status thermometer with the Unzip control isnearly identical to the approach used with the Zip control.The caption of the thermometer form can be set with theExtProgTitle property. Instead of ZipSubOptions, Unzipuses a numeric property called “UnZipSubOptions,”which, among other things, can be used to show a statusthermometer during the action being performed.

A number of #DEFINEs are associated with theUnZipSubOptions property, and they all begin with theprefix USO. The UnZipSubOptions property is a set ofbinary flags that can be configured by adding together the#DEFINEs for the required functionality. If I want to usethe DynaZIP thermometer with a cancel button and haveit reset the archived bit, I’d set UnZipSubOptions in thefollowing manner:

.UnZipSubOptions = USO_EXTERNALPROG + ; USO_EXTPROGCANCEL + ; USO_RESET_ARCHIVED

The UnZipSubOptions property can also be used towrite a log of the unzipping activity, ignore long filenames, and much more.

Other properties of interestHere are descriptions of a few more properties. (TheDynaZIP manual covers many others.)

• DecryptCode (Character): When items in a ZIP fileare encrypted, you must set this property to thepassword required to decrypt them. The string can beup to 65 characters in length. In addition to filling inthis property, it’s necessary to set the DecryptFlagproperty to .T. to enable decryption.

• DiagnosticFlag: Setting this flag to .T. will cause theUnzip control to write out an ASCII text log file of alloperations to and from the Unzip control. The log file,named DYNAZIP.LOG, will be located in yourWindows directory. The information written outincludes the values of the different properties of thecontrol. This is very useful for troubleshootingproblems during development.

• OverwriteFlag: This is like a SET SAFETY OFF whenunzipping items. The Unzip control won’t promptyou to overwrite files if this property is set to .T.

• TestFlag: Setting this flag to .T. puts the Unzip controlinto a test mode that goes through all of the motionsof unzipping, including extracting the files, but thefiles are never written to disk. This is fantastic forverifying that each item in the ZIP file can beuncompressed without an error.

8 http://www.pinpub.comFoxTalk April 2000

ActionDZ Settings for UnzipUNZIP_C OUNTALLZIPMEMBERS

Returns the total number of items in the ZIP file to the

ReturnCount property. This count can be affected by the

USO_IGNORELONGNAMES bit of the UnZipSubOptions member.

UNZIP_GETNEX TZIPINFO

Retrieves information about the next item in the ZIP file. This

information is placed in the properties whose names begin with

“zi_,” including zi_attr (file attributes), zi_cMethod (method of

compression), zi_cOption (related to zi_cMethod), zi_cPathType

(whether a path was stored with this item), zi_crc_32 (CRC value),

zi_cSize (compressed size), zi_DateTime (datetime stamp),

zi_FileName (file name), zi_index (zero-based item number

within ZIP file), and zi_oSize (uncompressed size).

To retrieve information about all items in the ZIP file, first use

the UNZIP_COUNTALLZIPMEMBERS action to find the number of

items. Then use the UNZIP_GETNEXTZIPINFO action that

many times.

UNZIP_C OUNTNAMEDZIPMEMBERS

Retrieves the number of items in the ZIP file that match the

FileSpec property. This action returns the number of counted

items in the ReturnCount property. Other properties can affect

the final number of items that match the FileSpec.

UNZIP_GETNEX TNAMEDZIP INFO

Retrieves information from the ZIP file about the next item that

matches the FileSpec property. This information is placed in the

properties whose names begin with “zi_” (see the UNZIP_

GETNEXTZIPINFO entry for descriptions). Other properties can

affect the final number of selected items.

This is the companion to the UNZIP_COUNTNAMEDZIP

MEMBERS action. To retrieve information about all items in the

ZIP file that match a particular FileSpec, first use the UNZIP_

COUNTNAMEDZIPMEMBERS action to find the number of items.

Then use the UNZIP_GETNEXTNAMEDZIPINFO action that

many times.

UNZIP_GET COMMENT SIZE

Retrieves the number of characters in the comment of the ZIP

file. This is used to find the size of a comment, allowing you to

allocate the appropriate space to read back the comment. This

action returns the number of characters in the ReturnCount field.

UNZIP_GET COMMENT

Retrieves a comment string from the ZIP file. This action uses the

value of ReturnCount as a limit of how many characters may be

written into the ReturnString property. The number of characters

actually retrieved (from 0 to 2,047) is then returned in

ReturnCount. This is the companion to the UNZIP_GETCOMMENT

SIZE action. To retrieve the whole comment associated with a ZIP

file, first use the UNZIP_GETCOMMENTSIZE action to find out how

many characters are in the comment. Then use the UNZIP_

GETCOMMENT action to retrieve the comment.

UNZIP_GETINDEXEDZIPINFO

Retrieves information about an item (specified by UnZIPIndex) in

the ZIP file. The information retrieved is placed in the properties

whose names begin with “zi_” (see UNZIP_GETNEXTZIPINFO

for descriptions).

UNZIP_EX TRACT

Extracts (uncompresses) selected items from the ZIP file to the

directory specified by the Destination property.

UNZIP_FILET OMEM

Extracts (uncompresses) a portion of the selected item and

writes it in the string property UnZIPString. When using this

function, specify only a single item (without wildcards) in

FileSpec, or specify the item using the UnZIPIndex property.

The program UNZIPFXN.PRG is available in this month’s

Subscriber Download file at www.pinpub.com/foxtalk. In it, you’ll

find functions that demonstrate the use of these actions.

Things to watch out forRepeat this mantra: “Use full paths.” ActiveX controlsdon’t recognize VFP’s current directory setting. If youexecute the code to create a ZIP file, and the ZIP file isn’twhere you expect it to be or it doesn’t appear to have beenupdated, check to make sure that you included a full pathin the ZipFile property. The same can happen anywhereyou specify a filespec (ItemList, FileSpec, Destination, orsimilar specs).

One of the first things that I do when I unwrap a newActiveX control is to subclass it. You can subclass the Zipand Unzip controls just fine, but once you do, you can’t

instantiate the subclass using CREATEOBJECT()! It mustbe hosted on a form. I’ll leave a description of thisstruggle for the next article.

If you decide you can’t wait until next month toexplore some of the more advanced features, I’ll give youone warning. If you use any of the callback events, youshould set _VFP.AutoYield to .F. before setting theActionDZ properties; otherwise, the callback eventswon’t fire.

I’ve created several include files (.H files) that containthe #DEFINEs used by the DynaZIP controls (and used in

Continues on page 18

http://www.pinpub.com 9FoxTalk April 2000

FoxTalk

“clBackRest”: An ErgonomicSolution to a Common Painin the NeckChad J. Lemmer 6.06.0

Chad J. Lemmer describes clBackRest, a class he created tosimplify backups.

YOU’VE just arrived at work and your first phone callof the day comes in. “Our server took a viciousnosedive last night and I lost all of my data! Is there

any way you can get it back for me?” While one part of meis saying, “Sucks to be you!” (because you know theyhaven’t made a backup of their data in eons), another partof me sympathizes with them and makes me think,“Maybe if their backup procedure was a little easier in thefirst place, I could be enjoying my morning cup ofcappuccino right now instead of talking on the phone.”

Recently, I created a small application for my dad (a“newbie” to the wonderful world of computers) to keeptrack of the cows on the family farm. That’s right, a“Hometown Country Boy” with a cell phone and a pager.Scary, isn’t it? As I was working on completing theapplication, I realized I needed a simple way for my dadto back up his precious data, without introducing anyextra or unnecessary steps. Using the Windows Explorerto copy files to another disk would be too cumbersome.Creating a shortcut on the Desktop to a batch file thatwould copy the data files would create an extra step. Ineeded a way to back up data from within an application,either automatically when the application was closed orfrom a menu choice within the application. It needed to bepainless for the developer to integrate and simple for theend user to execute. As a result, I created the clBackRestclass.

Features of the clBackRest class include:• The ability to specify the source and destination

directories explicitly, or default them and allow theend user to change them.

• The ability to specify file extensions to include,excluding all other files.

• The ability to specify file extensions to exclude fromthe process.

• The ability to copy files to virtually any medium,

including floppy disks, ZIP disks, CD-Rs, or evenanother drive on your network to which you haveaccess. Tape drives aren’t supported.

• The ability to present the user with a list of optionaldrives if the destination directory doesn’t haveenough space left to fit all of the files to be copied.

• The ability to copy any type of file, not just FoxProdata files.

To use the clBackRest class, instantiate it by using theNEWOBJECT() or CREATEOBJECT() function. Then, setthe cSourceDirectory property to the directory from whichyou wish to copy the files, and set the cDestDirectoryproperty to the destination directory. If either of theseproperties is left empty, the user will be prompted for thedirectory to use. Additionally, you can use thelSourcePrompt and lDestPrompt properties to let usersoverride the directories specified in the cSourceDirectoryand cDestDirectory properties. For example, whenbacking up data files, you could set the cSourceDirectoryproperty to the application’s data directory and set thelSourcePrompt property to .F., because you probablydon’t want the user to select a different data directoryfrom which to copy the files. When restoring data files,though, you might want to do the opposite. In this case,you’ll set the cDestDirectory directory to the application’sdata directory, then set the lDestPrompt to .F., so that theuser doesn’t restore the files into the wrong directory.

You can also specify which types of files to includeand exclude by setting the cIncludeOnly and cExcludeproperties, using a comma-delimited list of fileextensions. If you specify any file extensions in thecIncludeOnly property, only files with those extensionswill be processed. If you specify any file extensions in thecExclude property, files with those extensions will simplybe skipped. If you leave both of them blank, all files willbe processed. Because the cIncludeOnly property takesprecedence over the cExclude property, you typicallywon’t use both properties at the same time. For example,

10 http://www.pinpub.comFoxTalk April 2000

if you specify any file extensions in the cIncludeOnlyproperty, it wouldn’t make much sense to specifyanything in the cExclude property, because only files withthe extensions specified in the cInlcudeOnly property aregoing to be processed.

To start the file copy process, you call the CopyFilesmethod. Before the files are processed, I execute a SETSAFETY OFF, so the user doesn’t get prompted tooverwrite the file if the file already exists in thedestination directory. If a file already exists in thedestination directory, it will be overwritten. The user iswarned of this behavior and has a chance to cancel beforethe copy process begins.

Table 1 is a list of the clBackRest class properties andtheir descriptions.

Table 1. clBackRest class properties and descriptions.

Property DescriptioncSourceDirectory The directory from which the files are copied. If

empty, the user will be prompted for thesource directory.

cDestDirectory The directory to which the files are copied. Ifempty, the user will be prompted for thedestination directory.

cIncludeOnly A comma-delimited list of file extensions toinclude. If specified, only files with theseextensions will be processed. If empty, all files willbe included, except those with the file extensionslisted in the cExclude property.

cExclude A comma-delimited list of file extensions toexclude. If specified, files with these extensionswill be skipped. If empty, all files will be includedunless the cIncludeOnly property specifies specificfile extensions to process.

lSourcePrompt If .T., the class will prompt the user to confirm thesource directory, allowing the user to change thesource directory from which the files are copied. If.F., the class will use the directory from thecSourceDirectory property without askingthe user.

lDestPrompt If .T., the class will prompt the user to confirm thedestination directory, allowing the user to changethe destination directory to which the files arecopied. If .F., the class will use the directory fromthe cDestDirectory property without askingthe user.

lAskForDrive If .T., the class will ask for another drive to use ifthe destination drive doesn’t have enough spaceto copy all the files.

cFileName (Protected) The name of the current filebeing copied.

nFileCount (Protected) Total number of files to be processed.

nTotalBytes (Protected) Total number of bytes to be processed.

aFiles (Protected) An array of the files to be processed,created by the ADIR() function.

When you call the CopyFiles method, the class willscan the files to be processed and compare the total bytes

to the amount of space left on the destination volume. Ifthe destination volume can hold the files to be processed,a message box appears. This box shows the number offiles and total bytes to be processed. The user can thenelect to continue or cancel. The class also checks thedestination volume to make sure it’s ready to accept filesvia the DriveOK method. The DriveOK method simplyuses the DISKSPACE() function, which will return –1 ifthere’s a problem accessing the volume. If the DriveOKmethod encounters a problem, a message box appears tolet the user know that the destination volume can’t beaccessed. The user has the option to try to correct theproblem and retry or abort the process.

The lAskForDrive property dictates what happens inthe event that there is insufficient space on the destinationvolume to fit all the files to be copied. If the lAskForDriveproperty is set to .T., the user will be presented with aform containing a list of removable drives on his or hersystem, excluding the original drive to which the classtried to copy the files. After all, you wouldn’t want theuser choosing the drive that failed in the first place! If thelAskForDrive property is set to .F., the copy process willbe aborted. The drive selection form is created byinstantiating the clGetDrive class, which is another classin the CLBCKRST.VCX class library. It uses API calls tobuild the list of removable drives. I’m not going to go intodetail about how the clGetDrive class works, as this isbeyond the scope of the article. Feel free to check it out tosee how it works.

Table 2 lists the methods and their descriptions fromthe clBackRest class.

Table 2. clBackRest class methods and descriptions.

Method DescriptionCopyFiles This is the method you call to get the ball rolling. It

will prompt the user to continue, validate the sourceand destination directories, and process the files.

DriveOK Checks the drive of a specified path to make sure it’sa valid drive and that it’s ready for file processing.

SpaceLeft Returns the amount of space left on the volume froma specified path.

GetDirectories Validates the source and destination directoriesspecified in the cSourceDirectory and cDestDirectoryproperties. If the cSourceDirectory or cDestDirectoryproperty is empty, this method will prompt the userto specify the directory using the GETDIR() function.The dialog box will default to the directoriesspecified in the cSourceDirectory andcDestDirectory.

ProcessFiles This method gets the file statistics of what will beprocessed and displays the statistics to the user withan option to continue or cancel. If the user decides tocontinue, this method will be called a second time tostart the file copy process.

ProcessOneFile This method is called by the ProcessFiles method tocopy a file.

http://www.pinpub.com 11FoxTalk April 2000

The following code will copy files from the datadirectory of an application to a floppy disk. It will alsoallow the user to specify an alternative destination. Itwon’t allow a user to change the source directory fromwhich the data files are copied. It will exclude all fileswith a BAK, TBK, or TMP extension.

oBackup = newobject("clBackRest", "clBackRest")with oBackup .cSourceDirectory = "c:\demoapp\data" .cDestDirectory = "A:" .lSourcePrompt = .F. .lDestPrompt = .T. .cExclude = "bak,tbk,tmp" .CopyFiles()endwithrelease oBackup

The following code will restore files to anapplication’s data directory from a floppy disk, and willallow the user to specify an alternative source from whichto retrieve the files, but it won’t allow him or her tochange the destination directory. It will restore only fileswith a DBF or FPT extension.

oRestore = newobject("clBackRest", "clBackRest")with oRestore .cSourceDirectory = "A:" .cDestDirectory = "c:\demoapp\data" .lSourcePrompt = .T. .lDestPrompt = .F. .IncludeOnly = "dbf,fpt" .CopyFiles()endwithrelease oRestore

If you’d like to take the clBackRest class for acomplete test drive, check out this month’s SubscriberDownload file, available at www.pinpub.com/foxtalk. Itcontains the CLBCKRST.VCX class library, along with ademo form allowing you to try out all of the options. Thedownload also contains my CLBASE.VCX andCLCTRL.VCX class libraries, because the classes used inthe CLBCKRST.VCX class library and demo form aresubclassed from classes in the CLBASE.VCX andCLCTRL.VCX class libraries.

LimitationsThe biggest limitation to the clBackRest class is the factthat it won’t span multiple disks. Most PCs have floppydrives, but not many current applications can fit all theirdata on a single floppy disk. The clBackRest class stillprovides that option for smaller applications. Actually,when I use the clBackRest class to back up my dad’s cowdata, I back up to a floppy disk because it’s a small systemthat probably won’t exceed the space available on a singlefloppy disk. If the files exceed the capacity of the floppydrive, all I need to do is change the cDestDirectoryproperty to the drive letter of the ZIP drive on his PCinstead of the floppy drive, and I’m back in business.

Because this class was designed to be used duringshutdown of the application, it doesn’t allow for thebackup of files that are currently in use. If you choose toprovide this functionality elsewhere in your application,it’s up to you, the developer, to ensure that the files beingcopied aren’t in use before you call the CopyFiles method.If, during the copy process, this class encounters a filethat’s currently in use, the user will be alerted with amessage box, the file will be skipped, and processing willcontinue with the next file.

The clBackRest class wasn’t designed to be a completesystem backup and restore solution. Rather, it wasdesigned to provide Visual FoxPro developers with asimple backup and restore solution to build into theirVisual FoxPro applications. The code is far from being“rocket science.” In fact, it’s nothing more than FoxPro’snative COPY FILE command, surrounded by errorchecking and an easy-to-use, easy-to-integrate interface.After all, every computer user—developers and end usersalike—deserves a good “backrest.” ▲

04LEMMSC.ZIP at www.pinpub.com/foxtalk

Chad Lemmer is a full-time I.S. programmer/analyst at Wausau Coated

Products in Wausau, WI, specializing in Visual FoxPro application

development. He also develops Visual FoxPro applications for the clients

of his own consulting business. [email protected].

Earn Money!Impress Your Friends!

See your name in print!

Go ahead, add a line to your résumé—“Published Articles.”If your tip shows up in the pages of FoxTalk,

we’ll send you $25. See the back page for theaddress where you can send your tips.

12 http://www.pinpub.comFoxTalk April 2000

http://www.pinpub.com 13FoxTalk April 2000

Manage Your ApplicationsDoug Hennig

This month, Doug Hennig presents a simple yet useful tooland discusses several reusable techniques he made use ofduring its design.

IF you’re like me, your hard drive (or your server ’shard drive) is littered with project directories: finishedapplications, in-progress client work, and other files.

Managing and switching among all these directories canbe a drag (“Was that project in T:\Apps\Clients\ACMERentals\SalesApp or F:\Projects\Current\SalesApp?”).

Recently, I had to do some remedial work on someFoxPro 2.6 applications (I bet we’ve all been doing someof that in the past year or so <g>), and I was reacquaintedwith an old friend: an application manager I’d writtenyears ago and forgotten about. It installed in the FoxProsystem menu and was a single keystroke away. Thisapplication manager was very simple: It listed, by adescriptive name, each application I’d registered with it,and it had functions to select an application, add or editinformation about an application, or remove anapplication from the list. When you selected anapplication, the application manager made thatapplication’s directory the current one and automaticallyopened the project file. Switching between applicationswas a piece of cake: Ctrl-L (to invoke the applicationmanager), End (to highlight the last application in the list,for example), and Enter (to make that application’sdirectory the current one and open its project file).

Other developers take similar approaches that areimplemented a little differently. For example, FoxTalkeditor Whil Hentzen adds a new pad to the end of theVFP system menu that lists all of his projects as items inthe menu.

Once I started using my FoxPro 2.6 applicationmanager again, I just had to use something similar in VFP.So, this month’s article is about my shiny new applicationmanager. “That sounds like kind of a simple topic,” youmight be thinking. Perhaps, but it’s a useful tool, andwe’ll encounter some interesting and helpful techniquesalong the way.

The application manager table and formFigure 1 shows the application manager form when it’srunning. It has a list of applications registered with themanager and buttons to select, add, edit, and remove anapplication. Double-clicking or pressing Enter in the listacts like clicking on the Select button.

Applications are registered with the manager byadding records to APPMGR.DBF. This simple table hastwo fields: NAME, the descriptive name of theapplication; and PROJECT, the path and name of theproject file for the application.

APPMGR.SCX is the application manager form. ItsLoad method checks for the existence of APPMGR.DBF,then creates it if necessary before opening it.

local lcDirectorydodefault()lcDirectory = substr(sys(16), at(' ', sys(16), 2) + 1)lcDirectory = addbs(justpath(lcDirectory))if not file(lcDirectory + 'APPMGR.DBF') create table (lcDirectory + 'APPMGR') (NAME C(60), ; PROJECT C(128)) index on upper(NAME) tag APPMGRendif not file(lcDirectory + 'APPMGR.DBF')use (lcDirectory + 'APPMGR') order APPMGR again shared

How do we know where to look for APPMGR.DBF?We expect it’s in the same directory as APPMGR.APP, sowe can use SYS(16) to give us the name of the currentlyexecuting method and the file (including path) it’s in. Inthis case, we’ll get “PROCEDURE FRMAPPMGR.LOAD<your path>APPMGR.SCT.” So, to get the path, we can’tjust use JUSTPATH(), because that would give us“PROCEDURE FRMAPPMGR.LOAD <your path>.”Instead, we’ll use JUSTPATH() on everything after thesecond space (“<your path>APPMGR.SCT”), and useADDBS() to ensure there’s a trailing backslash.

The Init method calls the custom GetApps method,which performs a SQL SELECT from APPMGR.DBF intothe aApps array property of the form. The lstApps list boxon the form has this array as its RowSource, so it lists theapplication descriptions.

6.06.0

Figure 1. The application manager.

Reuseable Tools FoxTalk

14 http://www.pinpub.comFoxTalk April 2000

The DblClick method of the list box and Click methodof the Select button both call Thisform.SelectApp. Thismethod tries to change the current directory to the onecontaining the project file for the selected application,then open the project. It tries to handle potentialproblems, such as the directory not existing or the projectfile not found. For example, if the directory exists but theproject file doesn’t, this method looks in the directory tosee if there are any project files. If there are no projectfiles, it gives an error message. If there’s exactly one, itopens that one (on the assumption that the project file wasrenamed after it was registered with the applicationmanager). If there are multiple project files, an Open Filedialog box is presented, allowing you to pick the projectfile to open.

The Click method of the Remove button callsThisform.RemoveApp, which simply deletes the recordfor the selected application and calls GetApps again torefresh the array and list box.

The Click methods of the Add and Edit buttons bothcall Thisform.EditApp, Add passing it .T. to indicate thatwe’re adding rather than editing. EditApp runs a formcalled EDITAPP.SCX to get the description of theapplication and its project file. However, notice that we’llneed three return values from EDITAPP.SCX: thedescription, the project, and whether the user chose OK orCancel. Because there can only be one return value, howdo we handle this? My preferred mechanism is to use aparameter object, one whose sole purpose is to be movedbetween methods with a “package” of parameters in itsproperties. These parameters are two-way; either thecaller or the method being called can examine orchange them.

Because both the caller and the method being calledmust know the names of the properties in the parameterobject, you could create a parameter-object class for thisuse. However, if you use this technique a lot, you’dquickly find yourself creating a lot of these classes. Beinga lazy, er, efficient programmer, I prefer to use a singlegeneric class (SFParameter in SFCTRLS.VCX) for this.This class has a This_Access method that automaticallyfires whenever anything tries to access a property of theobject. The code for that method is as follows:

lparameters tcMemberif not pemstatus(This, tcMember, 5) This.AddProperty(tcMember)endif not pemstatus(This, tcMember, 5)return This

This code does something pretty cool: If the propertybeing accessed doesn’t exist, it’s created. This means thateven though this object doesn’t have, for example, acMessage property, we can do the following:

loProperties = createobject('SFParameter')loProperties.cMessage = 'Howdy'

In other words, this class exposes a variable interface; itlooks like anything anyone wants it to!

Okay, back to the EditApp method. The first task is tocreate an SFParameter object and fill its (created on thefly) properties with either the description and project filename for the selected application or empty values if we’readding an application. Next, if we’re editing anapplication, we position the APPMGR table to the recordfor the application; this allows EDITAPP.SCX to ensure wedon’t enter a duplicate record. Then we callEDITAPP.SCX, passing it the parameter object. Uponreturn, we do one of two things. If the user chose Cancel,we do nothing; if the user chose OK, we create or updatethe record in APPMGR and call GetApps to refresh theapplication array and list box. In the case of adding anapplication, we also ensure the new application ishighlighted in the list box.

lparameters tlAddlocal lcAppName, ; lcProject, ; loProperties

* Create a properties object and fill it with either the* name and project for the selected app or blanks for a* new app.

lcAppName = iif(tlAdd, '', ;trim(Thisform.aApps[Thisform.lstApps.ListIndex, 1]))

lcProject = iif(tlAdd, '', ;trim(Thisform.aApps[Thisform.lstApps.ListIndex, 2]))

loProperties = MakeObject('SFParameter', 'SFCtrls.vcx')loProperties.cAppName = lcAppNameloProperties.cProject = lcProjectloProperties.lNew = tlAddloProperties.lSaved = .F.

* If we're editing an app, position the APPMGR table to* the record for the selected app.

if not tlAddseek upper(loProperties.cAppName)

endif not tlAdd

* Run the EditApp form. If the user saved his or herchanges,* either add or edit the app information.

do form EditApp with loPropertiesdo case case not loProperties.lSaved case tlAdd insert into APPMGR values (loProperties.cAppName, ; loProperties.cProject) This.GetApps() lnApp = ascan(This.aApps, loProperties.cAppName) if lnApp > 0 This.lstApps.ListIndex = asubscript(This.aApps, ; lnApp, 1) endif lnApp > 0 otherwise replace NAME with loProperties.cAppName, ; PROJECT with loProperties.cProject This.GetApps()endcase

EDITAPP.SCX is a simple form: It has only a couple oftext boxes, so you can enter the application descriptionand project filename. Its Init method accepts theparameter object passed in and stores a reference to it inThis.oProperties. The Click method of the OK button

http://www.pinpub.com 15FoxTalk April 2000

(which is an instance of SFOKButton in SFBUTTON.VCX,thus giving it a standard appearance) puts the descriptionand project filename into the cAppName and cProjectproperties of the parameter object; sets its lSaved propertyto .T., so the EditApp method will know the user choseOK; and then releases the form. There’s no code in theCancel button, since it’s an instance of SFCancelButton(also in SFBUTTON.VCX), which has Thisform.Release()in its Click method.

One useful feature of EDITAPP.SCX is a button thatdisplays an Open File dialog box (using GETFILE()) so theuser can select a file. This button is an instance ofSFGetFile (in SFBUTTON.VCX). Rather than having toremember all of the parameters for GETFILE() and codewhat to do after the user selects a file, you simply dropthis class on a form and set a few properties. ThecExtensions, cText, cOpenButton, nButtonType, andcCaption properties represent the appropriate parametersfor GETFILE(); cResult contains the name of the locationinto which you wish to store the filename the userselected; and cAfterDone contains the name of a methodor function to execute after the file dialog box is closed. Inthe case of the instance used in EDITAPP.SCX,cExtensions contains “PJX” (because we want to selectonly project files), cResult contains“Thisform.txtProject.Value” (because that’s where thefilename should be placed), and cAfterDone contains“Thisform.RefreshForm()” (which, while refreshing theform, will enable the OK button if the application namewas also entered).

Main programAPPMGR.PRG is the main program for the applicationmanager APP file. It consists of a main routine and a fewsubroutines. The purpose of the main routine is twofold:to install the application manager in the VFP systemmenu, and to run the application manager (I prefer tohave an app be self-contained rather than having theinstallation and execution code in separate places). Whichof these tasks is performed depends on how the programis called. If no parameters are passed, or if “INSTALL” ispassed (in other words, you used DO APPMGR.APP orDO APPMGR.APP WITH “INSTALL”), this routine willcall the InstallAppMgr subroutine to install theapplication manager in the menu. If “RUN” is passed(which is what happens when you choose “ApplicationManager” from the menu, as we shall soon see), theRunAppMgr subroutine is called to run APPMGR.SCX.Here’s the code for the main routine:

lparameters tcAction, ; tcPopuplocal lcCurrTalk, ;

lcAction

* Save the current TALK setting and set it off.

if set('TALK') = 'ON'set talk offlcCurrTalk = 'ON'

elselcCurrTalk = 'OFF'

endif set('TALK') = 'ON'

* Handle the action parameter: if it isn't passed, we'll* assume "INSTALL".

lcAction = iif(vartype(tcAction) <> 'C' or ; empty(tcAction), 'INSTALL', upper(tcAction))do case

* If this is an "install" call, do the installation.

case lcAction = 'INSTALL' do InstallAppMgr with tcPopup

* Run the application manager.

case lcAction = 'RUN' do RunAppMgrendcase

* Clean up and return.

if lcCurrTalk = 'ON' set talk onendif lcCurrTalk = 'ON'return

Notice that this routine can accept two parameters:the “action” (“RUN,” “INSTALL,” or nothing, whichmeans “INSTALL”) and the name of the menu popup intowhich to install the application manager. As we’ll see in amoment, the default popup is _MSM_TOOLS, the popupfor the Tools menu, but you can pass the name of anotherpopup if you want it installed under a different menuitem. Note that it must be the popup name, not theprompt that appears in the menu; see “System MenuNames” in the VFP Help file for a list of the names.There’s another good reason for passing the popup, even_MSM_TOOLS, as I’ll discuss shortly.

The RunAppMgr subroutine, called when “RUN” ispassed to APPMGR.APP, is very simple: It consists solelyof DO FORM APPMGR. I could have placed this codedirectly into the main routine rather than calling asubroutine, but this approach is more modular.

The main routine calls InstallAppMgr to add theapplication manager to the system menu. This routinedefines a couple of constants that specify the prompt toappear in the menu (including the hot key for it) and thepopup to install in if one isn’t specified (_MSM_TOOLS).The first task of this routine is to determine in whichdirectory this program is running. We need it so we canhard-code the path to find APPMGR.APP into the menu;after all, we don’t want to have the directory in the VFPpath, and it certainly won’t be in the current directory.This uses the same technique involving SYS(16) Idiscussed earlier.

lparameters tcPopuplocal lcDirectory, ; lnBar, ; lcBar, ; llPopup, ;

16 http://www.pinpub.comFoxTalk April 2000

lcPad, ; lnI#define ccMENU_BAR_PROMPT 'Application Manager'#define ccMENU_PAD '_MSM_TOOLS'lcDirectory = substr(sys(16), at(' ', sys(16), 2) + 1)lcDirectory = addbs(justpath(lcDirectory))

The next thing to do is to see whether an item for theapplication manager has already been added to the menu.This might happen if, for example, you run this programmore than once. We wouldn’t want to have two items forthe application manager in the menu, so if we find one,we’ll just reuse it. We’ll use CNTBAR() to get the numberof bars in the popup, then step backwards through them.Since the application manager item is added at the bottomof the menu, this is faster than starting at the top andworking down. GETBAR() gives us the number of a bar ina menu based on its position, and PRMBAR() gives us theprompt of the specified bar. Since PRMBAR() gives us theprompt without any hot key characters (\<), we’llcompare the prompt against the desired prompt strippedof these characters.

lnBar = 0lcBar = strtran(ccMENU_BAR_PROMPT, '\<')llPopup = vartype(tcPopup) = 'C' and not empty(tcPopup)lcPad = iif(llPopup, tcPopup, ccMENU_PAD)for lnI = cntbar(lcPad) to 1 step -1 if prmbar(lcPad, getbar(lcPad, lnI)) = lcBar lnBar = lnI exit endif prmbar(lcPad ...next lnI

If we didn’t find the application manager in themenu, we’ll add a separator line and a new bar at the endof the menu. We’ll then define what to do when the itemis chosen. Notice that the directory is hard-coded into themenu using macro expansion; this is necessary becausewe can’t use the lcDirectory variable when the menu itemis chosen, since that variable won’t exist. We also useLOCFILE() if for some reason VFP can’t findAPPMGR.APP where it was originally located.APPMGR.APP will be passed “RUN,” so the main routinein APPMGR.PRG knows to run the application managerrather than install it.

if lnBar = 0 lnBar = cntbar(lcPad) + 1 define bar lnBar of &lcPad after _mlast prompt '\-' define bar lnBar + 1 of &lcPad after _mlast ; prompt ccMENU_BAR_PROMPTelse lnBar = lnBar - 1endif lnBar = 0on selection bar lnBar + 1 of &lcPad ; do locfile('&lcDirectory.APPMGR.APP', 'APP', ; 'Locate APPMGR.APP') with 'Run'if not llPopup set sysmenu saveendif not llPopupreturn

Notice something interesting at the end of this code:If no popup name is specified, we use SET SYSMENU

SAVE to ensure that SET SYSMENU TO DEFAULTdoesn’t wipe out the application manager item in themenu. However, we don’t do this if a popup name ispassed as a parameter. The reason for doing this issomething Toni Feltman of F1 Technologies pointed out tome: If you use more than one SET SYSMENU SAVE, youvastly increase your chance of getting a “menu managerinconsistency error” (which terminates VFP faster thanyou can say “GPF”). So, if you want to add several itemsto your VFP system menu, it’s best to leave the SETSYSMENU SAVE command until after you’ve added themall. Although I don’t like to mention products I’vedeveloped in my articles, a feature I recently added toStonefield Database Toolkit (SDT) is pertinent here:SDT.APP (which adds two items to the Tools menu)accepts the “action” and popup name parameters usingalmost the same code as APPMGR.PRG (in fact, Ishoplifted the code for APPMGR.PRG from SDT <s>). So,if you want to add both the application manager and SDTto your system menu, here’s the best way to do that:

do APPMGR.APP with 'Install', '_MSM_TOOLS'do SDT.APP with 'Install', '_MSM_TOOLS'set sysmenu save

This defers SET SYSMENU SAVE until all the customitems have been added to the menu. An example of whereyou might do this is in some program automaticallyexecuted when you start VFP (for example, I have_STARTUP = “VFPSTART.PRG” in my CONFIG.FPW andhave code I want executed every time VFP starts inthis PRG).

ConclusionAlthough this is a simple tool, I find myself using it atleast a dozen times a day (am I the only one who’s usuallyat about four levels of interrupt deep at any one time<g>?), so it saves me lots of time trying to remember theexact name of a directory, typing it correctly, and thenremembering (and typing correctly) the name of theproject file to open. In addition to a useful tool, I hope youalso picked up a few useful techniques. ▲

04DHENSC.ZIP at www.pinpub.com/foxtalk

Doug Hennig is a partner with Stonefield Systems Group Inc. in Regina,

Saskatchewan, Canada. He’s the author or co-author of Stonefield’s add-

on tools for FoxPro developers, including Stonefield Database Toolkit,

Stonefield Query, and Stonefield Reports. He’s also the author of The

Visual FoxPro Data Dictionary in Pinnacle Publishing’s The Pros Talk Visual

FoxPro series. Doug has spoken at the 1997, 1998, and 1999 Microsoft

FoxPro Developers Conferences (DevCon) as well as user groups and

regional conferences all over North America. He’s a Microsoft Most

Valuable Professional (MVP) and Microsoft Certified Professional (MCP).

[email protected].

http://www.pinpub.com 17FoxTalk April 2000

FoxTalk

Beyond the View DesignerJim Booth 6.06.0

The view designer makes creating views quite easy. Thelimitations, however, can be restricting. You might or mightnot have encountered the limitations of the VFP viewdesigner. If you haven’t, you will. For example, the viewdesigner isn’t capable of using the UNION option that VFPsupports. Jim Booth shows how can you get around theselimitations and still use the view designer to assist you.

SUPPOSE you’re working on a system that managessales information. A request arrives for a phonelisting that combines customers and employees in a

single list ordered alphabetically by name. You’d like touse the View Designer to assist you in creating the properSELECT command to produce the list.

The SELECT commandTo combine the records from the customer table with therecords from the Employee table, you’ll need to use theUNION option of the SELECT command. The problem isthat, while the View Designer doesn’t support the UNIONoption, the Visual FoxPro does support that option. Howcan you create a view with a UNION clause?

VFP has the CREATE SQL VIEW command, whichallows you to create a view stored in a database inprogram code. A view created this way can use the fullimplementation of the SQL SELECT command. It wouldbe nice if we could get both the assistance that the ViewDesigner provides and the flexibility that the CREATESQL VIEW command gives us. And we can have both.

The View DesignerIn this example, we’ll use the TestData database that’s

included in the Visual FoxPro samples for our sampledata. That database includes two tables named Customerand Employee that we’ll be using.

In the VFP View Designer, we’ll create two localviews, one for the Customer table and one for theEmployee table. For customers, we’ll select the fieldsCompany (for the company name) and Phone (for thephone number). From the Employee table, we’ll select theexpression ALLTRIM(Last_Name) + “, “ + First_Nameand the Home_phone field.

We named the views lvCust and lvEmp, respectively,then saved them in the database.

How do we get the CREATE SQL VIEW code?VFP is installed with a utility program namedGenDBC.prg. This program is placed in a directory underthe VFP home directory named Tools\GenDBC.

If you open a database and then run GenDBC, theoutput of GenDBC is a program file that can create thedatabase that was open. Here’s a small extract from theprogram that GenDBC created.

DisplayStatus([Creating database...])CLOSE DATA ALLCREATE DATABASE 'TESTDATA.DBC'DisplayStatus([Creating table CUSTOMER...])MakeTable_CUSTOMER()DisplayStatus([Creating table PRODUCTS...])MakeTable_PRODUCTS()DisplayStatus([Creating table ORDITEMS...])MakeTable_ORDITEMS()DisplayStatus([Creating table ORDERS...])MakeTable_ORDERS()

The beginning of the program simply calls user-defined functions to create each element of the database.When we look further, we find the following lines inthe program.

DisplayStatus([Creating view LVCUST...])MakeView_LVCUST()DisplayStatus([Creating view LVEMP...])MakeView_LVEMP()

These are the lines that will run the code to create ourtwo local views. The actual code that creates the viewslooks like this:

FUNCTION MakeView_LVCUST***************** View setup for LVCUST ***************

CREATE SQL VIEW "LVCUST" ; AS SELECT Customer.company, Customer.phone ; FROM testdata!customer

Earlier VersionsIn earlier versions of VFP, GenDBC didn’t break the creation code

into functions. Instead, it produced one large program. In those

versions, it was possible to have a database that produced a

program file so large that it exceeded the VFP limitation of 64K in

procedure size. This resulted in a program that couldn’t be run.

VFP 6.0 fixed this by generating individual procedures for each

table and view. The code generated from the earlier GenDBC

could still be used as we have in this article.

18 http://www.pinpub.comFoxTalk April 2000

* Code extractedENDFUNC

FUNCTION MakeView_LVEMP***************** View setup for LVEMP ***************

CREATE SQL VIEW "LVEMP" ; AS SELECT ALLTRIM( Employee.last_name) ; + ", " + Employee.first_name, ; Employee.home_phone ; FROM testdata!employee

* Code extractedENDFUNC

The code that has been extracted from each of thesefunctions is a bunch of DBSetProp() function calls that setup the view. The important part of this is seeing theCREATE SQL VIEW syntax.

Now we’ll create a new program named PhoneList.Using the code generated by GenDBC, we can create thefollowing code in our new program:

* PhoneList.PRG***************** View setup for LVPHONELIST***************CREATE SQL VIEW "LVPHONES" ; AS SELECT Customer.company, Customer.phone ; FROM testdata!customer ; UNION ; SELECT ALLTRIM( Employee.last_name) ; + ", " + Employee.first_name, ; Employee.home_phone ; FROM testdata!employee ; ORDER BY 1

To create the new view, we must first open thedatabase and then run our program, as shown in thefollowing listing:

OPEN DATABASE TestDataDO PhoneList

This will create a local view in the TestData databasenamed lvPhones. When you browse the view, you’ll seethe customers and employees in alphabetical order byname with their respective phone numbers. The lvPhonesview is just like any other view in the database. The onlyproblem is that if you try to modify the lvPhones viewusing the View Designer, you’ll receive an error messagefrom VP informing you that the “SQL is too complex.”

SummaryGenDBC is a powerful utility in VFP. It has many uses,among them:

• Creating a program that can install a database andits tables

• Creating individual functions to create each table andview in a database

• Providing source code that can be modified andenhanced to accomplish complex tasks in a database

The utilities that come with VFP are often overlooked,and their true value typically goes unappreciated. This iscertainly true of GenDBC. ▲

Jim Booth is a Visual FoxPro developer and trainer. He has spoken at

FoxPro conferences in North America and Europe. Jim has been a

recipient of the Microsoft Most Valuable Professional Award every year

since it was first presented in 1993 and is a Microsoft Certified

Professional (MCP). He is co-author of Effective Techniques for Application

Development and Visual FoxPro 3 Unleashed and is contributing author for

Special Edition Using Visual FoxPro 6.0. He’s also the technical editor for

Database Design for Mere Mortals. 203-758-6942, www.jamesbooth.com,

[email protected].

code samples in this article). The files DZ32.H, DZ.H, andDUZ.H are available in this month’s SubscriberDownloads at www.pinpub.com/foxtalk. The file DZ32.Hhas #INCLUDEs for both the DZ.H and DUZ.H files. Ifyou aren’t sure which file to include, choose DZ32.H andyou’ll have everything.

My impressions of DynaZIP-AXI think that this is an amazing product, one that should beon the “Bat-belt” of any developer. It’s obvious that a lotof thought went into the creation of this product. Theinclusion of support for multi-threaded capabilities,DBCS, UNC, and international concerns shows a focus onkeeping pace with currently evolving technology and theglobal programming marketplace.

Stay tuned! Next month’s article will delve deeperinto the product, focusing on the features that providehooks into customization and internationalization, as wellas the ability to change the name and other information ofitems while zipping and unzipping. ▲

04HODDSC.ZIP at www.pinpub.com/foxtalk

Rick Hodder is a senior developer and class architect for Flash Creative

Management, a Hackensack, NJ-based consulting firm, where he has

served as a project mentor, technical lead, and worked on projects

including developing object-oriented business frameworks and Web-

based applications using Microsoft development tools. He’s well versed

in object-oriented analysis, as well as development and design patterns,

and gives presentations on design patterns in New York, New Jersey, and

Connecticut. He was a speaker for Visual FoxPro Devcon 1999, and he was

also selected as a speaker for Microsoft DevDays 1998. Before working at

Flash, Rick developed for Citibank, Brooks Brothers, and TIAA-CREF. He has

a BA in psychology and computer science from Notre Dame University

and is a Microsoft Certified Professional. [email protected].

Zipping and Unzipping . . .Continued from page 8

http://www.pinpub.com 19FoxTalk April 2000

The Kit Box FoxTalk

Our Department Needs You!Paul Maskens and Andy Kramek 6.06.0

Andy Kramek and Paul Maskens have been looking foradditional development staff recently and are findingsuccessful recruitment more difficult than they would havebelieved possible. So now they’re exploring just how theyshould assess someone.

Andy: How’s the recruitment drive been going? Youhaven’t asked me to sit in on any interviews in the pastcouple of weeks. Does that mean we’ve got all of thepeople we need now?

Paul: Not exactly. The problem is that, while it’s easyenough to get CVs from agencies, many people out thereare being sent in as VFP programmers but just aren’t up tothe job. Not only that, but on a bad day I can see the sameCV three times from three different agencies. (I’ve evenseen the same CV with different names on it!)

Andy: Well, I suppose you can’t stop agencies from tryinganything within their power to get their people in front ofyou. Though, to be fair to the agencies, it’s perfectlypossible that people register with several agencies.

Paul: But, then, shouldn’t the agency check with thecandidate before submitting the CV?

Andy: Yes, of course they should, and I think that mostresponsible agencies do so. After all, the agency’s maininterest is in getting one of their people employed by you,and they know that you’re not going to recruit someonewho’s not qualified, no matter how many times they sendin the CV. I suspect that the agency often doesn’t tell thecandidate the details (for fear of the candidate making adirect approach?) and just gets a “general” approvalto submit.

Paul: I suppose so, but the main problem lies in findingout whether candidates are competent. I mean, I can see aglowing CV with all of the right acronyms and buzzwordson it; then, when I actually talk to the person, he or shejust doesn’t live up to the CV.

Andy: Unfortunately, that’s true. I suppose that a face-to-face interview is the only real way of finding out. But Istill think that there are some things to look for in a CV,things that can give you a few clues.

Paul: Like what? Apart from the obvious…I mean,someone who hasn’t used VFP in the past 18 months andwhose recent contracts were for VB and SQL Server isn’tlikely to be current on VFP today. But he or she might stillbe a really good developer.

Andy: Well, I’ve always said that a competent developercan work in almost any language with a minimumof adjustment.

Paul: True; I learned OOP using Borland Pascal 7 beforeVFP 3 was a glint in Susan Graham’s eye. I couldprobably still write an ’86 assembler program with a listof the instruction set. The same has got to be true of otherpeople. So, we come back to the same question: Howdo I find a competent developer and weed outthe incompetent?

Andy: For a VFP developer, I’d start by scanning the CVfor things like certification; attending conferences;membership in a user group; references to Compuserve,UT, or newsgroups; published articles…in short, anythingthat leads me to think that the person knows more aboutthe subject than merely how to write a few lines ofprogram code.

Paul: If you look for all of those, you’re looking forus, Andy!

Andy: So? Don’t you think we’re competent? <g>

Paul: That’s not the point! Not every competentprogrammer has written a book or a magazine article.

Andy: All right, but let’s just consider those thingsin order.

Paul: Certification, then. I’m certified.

Andy: You certainly should be!

Paul: But I’m certified for things I’ve never done, too. Ipassed the VFP Distributed Applications exam, whichmight lead someone to think I know what I’m doing. Butin fact I just read the textbooks, attended the DevConsessions, and drank in the bar with the authors. I

20 http://www.pinpub.comFoxTalk April 2000

absorbed enough knowledge by osmosis to pass the examwithout ever having produced a distributed application.All I did was answer “multithreaded DLL” wherever itwas one of the multiple choices.

Andy: Somehow I doubt that was all that you did, Paul.But you’re making a fair point. Certification doesn’tguarantee competence, it merely ensures a minimum levelof acquaintance. I confess that one of the things thatmakes me uncomfortable about the current certificationsystem is the “industry” that has grown up providingself-study books and courses designed to get people topass the certification exam. Merely passing the exam hasbecome an end in itself! To my mind, that tends todevalue the qualification a little.

Paul: But that’s true of all product certifications. That’swhy I’m making the point (and you’re agreeing!) that allcertification does is show you passed the test.

Andy: Well, I did pass both of them, and at least I have thecomfort of knowing that I did it without “swotting” andthat my scores really were my scores.

Paul: Well you should pass. <g> However, I think we canagree that the most important thing about certification isthat it does prove that you’re serious enough about theproduct to bother taking the test.

Andy: And that’s not a bad thing. I suppose you could saythe same thing about attending conferences.

Paul: I agree, it’s showing a commitment to personaldevelopment. (Even if all you really attend for isthe parties.)

Andy: Oh, come on, just by being at conferences you gainknowledge—even if only, as you said, by “osmosis.” Thesessions aren’t the only place to learn things; plenty oftechnical discussion takes place at the bar and over meals.

Paul: True. Of course, the importance of networking can’tbe underestimated, either, and it’s really nice to meet oldfriends and make new ones. That brings us to usergroups, then.

This is a hard one to answer for the U.K., because wehave only one for the whole country. Some areas in theU.S. have more than one local user group. Of course, fromthe U.K., we can just nip across (over or under) theChannel and join the Dutch or German user groups. It’scloser than going out of state for some Americans! There’sa language barrier, though.

Andy: But again, what we’re talking about areparticipation and willingness to explore and expand one’s

knowledge and experience. “Ordinary” programmersdon’t do that, and you’re looking for “competent,” notmerely ordinary. People who are active in user groups are,in my experience anyway, precisely the people who wantto learn more and are willing to share what they knowwith others. Of course the growth of online groups has, tosome extent, undermined the traditional user group.

Paul: Yes; electronic media, particularly that Internetthing, are the place to find immediate and direct support,help, and advice. So if somebody actually usesCompuserve, UT, CiX, or newsgroups, then I’d see that asindicating that they know enough to recognize when theyneed help—and, very importantly, they also have enoughinitiative to go and find out the answers.

Andy: Agreed. That’s an excellent trait. Of course,knowing what F1 does and using it and having a shortcutto hackfox.chm on the desktop aren’t bad things, either.

Paul: Ah, that sounds like an interview question! Howcould we ask someone if they have “hackfox” withoutgiving the game away and feeding them the answer inthe question?

Andy: Try: “How many VFP-related shortcuts do youhave on your desktop?” I have eight, and none of themare to my applications—though I do have more than oneversion of VFP installed.

Paul: Good, but a supplemental question, “What arethey?” would be useful too. Now, moving on to articlespublished—isn’t that a little exclusive? There are only somany publications, and they print so few articles, thatonly a tiny percentage of developers get published.

Andy: But the thing is that the publishers are alwayslooking for new authors. And even if you don’t want towrite an article, you can get tips published. Again, itshows that an individual sees what he or she does as a“profession” rather than just a job.

Paul: And you’ve put your finger on the core of theproblem! What we’re really looking for are “developmentprofessionals,” and I don’t think that we’re being eitherunusual or unreasonable in that. There are plenty ofpeople who can write a program to a specification, butthere are fewer who can do the whole development job.

Andy: Hang on there—are you saying that a programmeris not a developer?

Paul: Interesting point; there is a difference. Being adeveloper involves more than just writing the code. Wedevelopers tend to be analysts, designers, programmers,

http://www.pinpub.com 21FoxTalk April 2000

and testers, which involves many different skills. Wemight even write the documentation.

Andy: So what you’re saying is that we’re jacks of alltrades and masters of none? After all, I know plenty ofanalysts who simply don’t want to write code; I alsoknow programmers who are totally incapable of doing arequirements analysis and wouldn’t want to do it anyway.

Paul: Yes, but in a different environment there wouldn’t bea need to have multitalented developers. You could havea team of business analysts, a team of system analysts,then designers, programmers, testers, and technicalauthors. They would all be specialists.

Andy: Remember the old saw “A camel is a horsedesigned by committee.”

Paul: We don’t have enough space in the column to talkabout what’s wrong with software development aspracticed in some large organizations, do we? Anyway, itdoesn’t have to turn out as a camel.

By the way, I wouldn’t agree that developers tend tobe “masters of none,” either. I wouldn’t claim to be a dataexpert; you do data (which is why we hired you). Marciadoes grids (though perhaps that says more about hertenacity than anything else <g>). My point is thatwhenever you have to employ more than one developer,you’re building a team. That team is going to be made upof developers (and analysts and programmers if it getslarge enough) whose strengths and weaknesses mustcomplement each other to produce a well-balanced group.

Andy: That’s a valid point, but we’re wandering awayfrom the technical issues there. Let’s get back to theoriginal point, which is how do we decide that somebodyis competent, either as a developer or as a programmer.Seems to me that we’ve pretty much ruled out relying onthe CV, other than as a tool to decide whom to interview.

Paul: I think that’s probably right, too. If someoneproduces a good CV, you still have to read between thelines to see whether it really hangs together, and thethings we’ve been discussing tend to providecorroborative detail.

Andy: So, having perused the CVs, we decide whom wewant to interview and then ask them a battery of personaland technical questions. Is that it?

Paul: I like the idea of telephone screening too, though it’sstill pretty rare in the U.K. Whil Hentzen’s “dreaded 24”questions would be ideal for ruling people in or out veryquickly. Or do you think that should be sent out, or e-mailed, to slim down the list before interviewing?

Andy: I’m not sure I like the idea of sending out questionsto people, if only because you can get the answers to anyset of questions using books, manuals, Help files, and soon, without actually understanding what the answersmean. Although I suppose that if you have the books andmanuals to hand, that in itself says something.

Paul: That’s why I like Whil’s questions; they’rechallenging on several levels and aren’t merely technicalquestions. Like: “Tableupdate can be passed twoparameters. Describe what each does.”

Andy: I’m sorry, but I don’t understand the question.TableUpdate() takes up to four parameters; which two areyou talking about?

Paul: Good, you passed. You know it’s wrong, you havethe confidence to tell me so. You did it so politely, too. Youeven get bonus points for knowing the right answer. Next,how about “Name the controls in VFP that can’t besubclassed”; again, will you tell me the question’s wrong?

Andy: Yes, did you doubt it? Of course there are nocontrols that can’t be subclassed. Although some can onlybe subclassed programmatically. But I suppose that’swhat the question is asking.

Paul: Yes, in a roundabout way and without giving awaythe answer. It reveals more about the candidate than itfirst appears, particularly when you look at the questionknowing the answer.

Andy: I don’t think we should answer the rest of the 24;otherwise, we won’t be able to use them in the future. ButI think it’s important to emphasise that, to use themeffectively, you have to know both the answers and thereason for asking the question. Then work out what theanswers reveal about your candidate. What do you thinkabout practical testing at interviews?

Paul: You mean something like “create a data entry formfor that table, which will add, edit and delete records”?

Andy: Yes, precisely.

Paul: Worst case is they’ll refuse to do it, or they’ll use thewizards, which doesn’t prove anything anyway.

Andy: In the first case, I’d say you’ve learned quite a lotabout the person right there <g>. For the second, I’d makesure to give the candidate a machine that doesn’t have thewizards installed.

I don’t think it’s unreasonable to ask somebody todemonstrate the skills that he or she has, so long as the“test” isn’t merely an excuse to get some free consultancy.

22 http://www.pinpub.comFoxTalk April 2000

I’d also suggest that candidates be warned in advancethat they’ll be asked to take a practical test.

Paul: I agree with both of those points. You shouldn’t askinterviewees to solve your business problems for you.Nor should you expect people to spend hours doingsomething, unless you prepare them for it.

Andy: Perhaps at this point a summary of our discussionis appropriate. We started out by wondering what to lookfor, how to judge a CV. We then talked about the technicalquestions we’d use at an interview.

Paul: Right, then; now to the meat of this month’s column.How about some new interview questions? Both you andI were complaining after taking the certification examsthat many questions were overly verbose and complex, soI guess this is our chance to present questions our way.

Andy: Okay, then, should we publish the rationale and theanswers too?

Paul: Definitely not! If someone wants them, he or she canalways contact us. Here are “the Kit Box 15”:

1. What’s the difference between CREATEOBJECT(),ADD OBJECT, and ADDOBJECT()?

2. What will the value of “x” be after running thefollowing code:

LOCAL X X = "Apples" DO Y

FUNCTION Y X = 'Pears' RETURN

3. What will the value of “x” be after running thefollowing code:

LOCAL X X = "Apples" DO Y WITH X

FUNCTION Y LPARAMETERS X X = 'Pears' RETURN

4. How would you describe your framework?5. How many of the 700 or so numbered VFP errors can

you successfully recover from programmatically, andallow execution to continue?

6. Why does a form’s BufferMode property accept onlyvalues 0, 1, 2 when CURSORSETPROP() acceptsvalues 1, 2, 3, 4, 5 for BUFFERING?

7. When must you use TYPE() instead of VARTYPE()?8. What’s wrong with the following code?

IF NOT SEEK( lcToFind ) lcStatus = GETFLDSTATE( -1 ) llChanged = EMPTY( CHRTRAN( lcStatus, "24", "" )) ENDIF

9. What naming standards do you use?10. Why is LISA important?11. Why is it sometimes necessary to issue

TABLEUPDATE() twice on a view?12. Why would you use DO FORM ... NAME ... LINKED

instead of DO FORM ... TO ...?13. A text box VALID() method calls a modal form,

which has an edit box and two command buttons.The form’s INIT() method sets focus to the secondcommand button. What happens when the user tabsout of the text box?

14. List all of the possible reasons that could cause anerror message to appear.

IF NOT THISFORM.ADDOBJECT( 'NewObj', 'MyClass' ) WAIT WINDOW "Unable to add new object" NOWAIT RETURN .F. ENDIF

15. Why is NODEFAULT a command, butDODEFAULT() a function? ▲

Andy Kramek is a longtime FoxPro developer, FoxPro MVP, independent

contractor, and occasional author based in Birmingham, England. He

currently works at Euphony Communications Ltd.

[email protected].

Paul Maskens is a VFP specialist and FoxPro MVP, working as

development manager for Euphony Communications Ltd. He’s based in

Oxford, England. [email protected].

http://www.pinpub.com 23FoxTalk April 2000

vertical position, and other features commonly found inmainstream monitors.

The exam will also test the use of the Display appletin Control Panel, including the use of wallpapers, screensavers, color schemes, color palettes, font sizes, andresizing the desktop area.

Exams 69-201 through 204 are all variations onInstalling, Configuring and Operating the MicrosoftKeyboard. The four exams are for each version of thekeyboard, including the Internet Keyboard, InternetKeyboard Pro, Natural Keyboard Pro, and NaturalKeyboard Elite.

The user will be expected to identify all 26 letters ofthe alphabet, the 10 numbers from 0 to 9, and each of thecharacters that cartoonists use to illustrate theircharacters’ profanity. The user will be expected to identifythe difference between the backspace and delete keys, thebackspace and left cursor arrows, and all of thosespecialized keys. Identification of the Any key will also berequired when appropriate.

Exams 69-301 through 69-309 are all variations onInstalling, Configuring, and Operating the MicrosoftMouse. Each exam is for a particular version of mouse:Mouse, Basic Mouse, Wheel Mouse, Cordless WheelMouse, IntelliMouse, IntelliMouse Pro, IntelliMouseTrackBall, IntelliMouse with IntelliEye, andIntelliMouse Explorer.

These exams will cover the proper placement of themouse; the placement of the hand and fingers over thevarious buttons, keys, and other gizmos on the mouse;and proper clicking, double-clicking, triple-clicking, andprofessional quad-clicking techniques. The user will beexpected to be equally proficient with left- and right-handed mice, in the event that the mouse he or she uses isplaced on either the left or right side of the keyboard.

Get involved!You can get involved with these new exams in one of twoways. The first way is by submitting questions for theexam. You can do this at any of the major conferences in2000, including Fox DevCon in Miami, Tech-Ed inHouston, Windows World in New York City, and VBits inSan Francisco.

You can also write the beta exam and providefeedback on the questions that make it to the exam. Asthis exam covers Microsoft hardware, you’ll need to bringyour own specialized hardware for parts of the exam. Inother words, you’ll need to bring two sharpened #2pencils with unused erasers.

Tread lightly!Naturally, people working on or taking the beta exams areunder NDA with regard to discussing the exams withothers not under NDA. However, emotions can run highduring the beta period of a new Microsoft SKU, so youshould be aware that not everyone in the industry ispleased with Microsoft’s foray into the certification of endusers. Specifically, many Linux and BeOS zealots areopenly dismissive. “Certify a keyboard? What’s next?Requiring programmers to be certified in English so theywrite comments with good grammar? Puh-leease!”

Richard Stallman, Open Source Advocate and generalsoftware weirdo, said, “We don’t believe these testsshould be exclusive to Microsoft developers and users.Everyone who scrolls a mouse or taps a keyboard shouldbe allowed to take these exams. And, in the spirit of theOpen Source movement, we also believe that thequestions on the exam—and the answers—should befreely available to anyone who has the capability todownload them. The whole concept of knowledge beingrestricted to a certain ‘elite’ smacks of yet another plot ofthe Establishment.”

Thus, be careful to whom you talk about the exams—keep your friends close and your enemies closer.

The deadline to apply is April 1, 2000. ▲

New Certification Exams . . .Continued from page 2

Downloads• 04HODDSC.ZIP—Source code for Richard Hodder’s article,

“Zipping and Unzipping for Clients.”

• 04LEMMSC.ZIP—Source code for Chad Lemmer’s article,

“‘clBackRest’: An Ergonomic Solution to a Common Pain in

the Neck.”

• 04DHENSC.ZIP—Source code for Doug Hennig’s article,

“Manage Your Applications.”

Extended A rticles

April Subscriber Downloads• 04CONWAY.HTM—“The Mere Mortals Framework 6.0” by

Kelly Conway. A review of Oak Leaf ’s flexible framework.

• 04COOLTL.HTM—“A New Spin on Thermometer Bars” by

Whil Hentzen. Our editor continues his series on Cool Tools.

• 04COOLSC.ZIP—Source code to accompany Whil Hentzen’s

“A New Spin on Thermometer Bars.”

• 04WOOD.HTM—“Third Party Tool Forum: Controlling User

Access to Application Data in Foxfire!” by Bill Wood.

24 http://www.pinpub.comFoxTalk April 2000

User name

Password

gloomy

hamper

The Subscriber Downloads portion of the FoxTalk Web site is available to paidsubscribers only. To access the files, go to www.pinpub.com/foxtalk, click on“Subscriber Downloads,” select the file(s) you want from this issue, and enter theuser name and password at right when prompted.

FoxTalk (ISSN 1042-6302) is published monthly (12 times per year)by Pinnacle Publishing, Inc., 1503 Johnson Ferry Road, Suite 100,Marietta, GA 30062. The subscription price of domesticsubscriptions is: 12 issues, $179; 24 issues, $259. POSTMASTER: Sendaddress changes to FoxTalk, PO Box 72255, Marietta, GA 30007-2255.

Copyright © 2000 by Pinnacle Publishing, Inc. All rights reserved. Nopart of this periodical may be used or reproduced in any fashionwhatsoever (except in the case of brief quotations embodied incritical articles and reviews) without the prior written consent ofPinnacle Publishing, Inc. Printed in the United States of America.

Brand and product names are trademarks or registered trademarksof their respective holders. Microsoft is a registered trademark ofMicrosoft Corporation. The Fox Head logo, FoxBASE+, FoxPro, andVisual FoxPro are registered trademarks of Microsoft Corporation.FoxTalk is an independent publication not affiliated with MicrosoftCorporation. Microsoft Corporation is not responsible in any way forthe editorial policy or other contents of the publication.

This publication is intended as a general guide. It covers a highlytechnical and complex subject and should not be used for makingdecisions concerning specific products or applications. This

publication is sold as is, without warranty of any kind, either expressor implied, respecting the contents of this publication, includingbut not limited to implied warranties for the publication,performance, quality, merchantability, or fitness for any particularpurpose. Pinnacle Publishing, Inc., shall not be liable to thepurchaser or any other person or entity with respect to any liability,loss, or damage caused or alleged to be caused directly or indirectlyby this publication. Articles published in FoxTalk reflect the views oftheir authors; they may or may not reflect the view of PinnaclePublishing, Inc. Inclusion of advertising inserts does not constitutean endorsement by Pinnacle Publishing, Inc. or FoxTalk.

Direct all editorial, advertising, or subscription-related questions to Pinnacle Publishing, Inc.:

1-800-788-1900 or 770-565-1763Fax: 770-565-8232

Pinnacle Publishing, Inc.PO Box 72255

Marietta, GA 30007-2255

E-mail: foxtalk@pinpub .com

Pinnacle Web Site: http://www .pinpub .com

FoxPro technical support:Call Microsoft at 425-635-7191 (Windows)

or 425-635-7192 (Macintosh)

Subscr iption r ates:United States: One year (12 issues): $129; two years (24 issues): $232

Canada:* One year: $144; two years: $247Other:* One year: $149; two years: $252

Single issue r ate: $17.50 ($20 in Canada; $22.50 outside North America)*

Editor Whil Hentzen; Publisher Robert Williford;

Vice President/General Manager Connie Austin;

Executive Editor Heidi Frost; Managing Editor Robert Hatch;

Copy Editor Andrew McMillan

European ne wsletter orders :Tomalin Associates, Unit 22, The Bardfield Centre,

Braintree Road, Great Bardfield,Essex CM7 4SL, United Kingdom.

Phone: +44 1371 811299. Fax: +44 1371 811283.E-mail: [email protected].

Australian ne wsletter orders:Ashpoint Pty., Ltd., 9 Arthur Street,

Dover Heights, N.S.W. 2030, Australia.Phone: +61 2-9371-7399. Fax: +61 2-9371-0180.

E-mail: [email protected]: http://www.ashpoint.com.au

* Funds must be in U.S. currency.

FoxTalk Subscription Information:1-800-788-1900 or http://www.pinpub.com

http://www.pinpub.com 1FoxTalk Extended Article: April 2000

FoxTalkSolutions for Microsoft® FoxPro® and Visual FoxPro® Developers

This is an exclusive supplement forFoxTalk subscribers. For more

information about FoxTalk, call us at1-800-788-1900 or visit our Web

site at www.pinpub .com/foxtalk.

Extended Article

The Mere MortalsFramework 6.0Kelly Conway

Looking for a Visual FoxPro application framework designedto help you create object-oriented applications that scalefrom the desktop to n-tier? This month, Kelly Conway reviewsthe flexible framework from Oak Leaf Enterprises.

FROM its beginnings as a step-by-step guide to anexisting framework, the Mere Mortals Framework(MM) has evolved into a complete Visual FoxPro

application framework. The original Codebook For MereMortals guide was a Word document that walkeddevelopers through the steps to create applications basedon the framework made popular by Alan Griver’s bookThe Visual FoxPro 3 Codebook. Still based on Codebook,MM Version 6.0 adds a wealth of features anddocumentation that will help VFP developers createhighly flexible, object-oriented applications.

FeaturesLet’s start by listing and discussing the features that willmost likely help you decide whether you should considerusing MM as your application framework. As always,there are more features in the product than can bethoroughly discussed in this article. Also, as with mostframeworks, the product is a moving target. So don’tassume that the framework doesn’t support a feature thatyou are looking for merely because I don’t list it here. It’squite possible that your favorite feature has been addedsince this review, or that I merely failed to mention it.Please contact the vendor (see the sidebar) for a complete,up-to-date list of all features.

Application setupMM’s QStart utility provides a quick method for creatingframework subclasses for your next application. LikeCodebook, MM creates your application based on the

contents of a project named Generic. By altering thecontents of that project, you can change the defaultmakeup of new applications that you use QStart to create.Items created include your application’s directorystructure, database container, framework tables,framework subclasses, default menu, configuration tables,and project files.

Business objectsThe MM framework places a heavy emphasis on businessobjects. The middle tier of a typical three-tier architecture,these classes model the business entities and encapsulatethe business rules. They’re also responsible for separatingthe user interface from the data source, and allowing forreuse of business logic and data sources across multipleuser platforms (or for reuse of business logic and userinterface components across multiple data sources).

Business objects aren’t found only in VFP or thisframework. Instead, the framework implements awidespread construct found in object-oriented designsthat are implemented in a number of languages. In fact,some of the best books about business objects are writtenfor Visual Basic. You can get a great introduction to them,as well as the details of how to create and use them inyour applications, by reading the 100+ pages dedicated tothem in the framework’s Developer Guide.

Business rulesOne purpose of business objects is to apply business rules(for example, “Ensure that all customers have a name,address, and phone number,” “Don’t accept an order fromcustomers who are over their credit limits,” “Expectedorder shipment dates must be today or later”). Inmonolithic applications, these business rules aresquirreled away inside forms, programs, and reports. MM

6.06.0

2 http://www.pinpub.comFoxTalk Extended Article: April 2000

business rules classes can be attached to a business object,which prompts the business object to ask the rules objectto validate any data before it’s saved. This concept issimilar to the reason for business objects—it emancipatesa business’ most important (and most often changing)code and allows it to be used in multiple applications,even if they’re not all written in VFP.

Event objectsAnother concept promoted in the Developer Guide(although not nearly as much as business objects) is thatof event objects. These classes instantiate multiplebusiness objects and encapsulate their behavior inperforming some action that involves them all. Forexample, a Sale event object might create instances ofCustomer, Invoice, and Product business objects. TheExecute() method within the Sale class would coordinatecalls to methods of those classes to make a sale. Buildingyour application from components like these can makethem highly reusable, flexible and maintainable.

Data accessFlexible data access is one of the biggest features of thisframework. By doing the work to create separate userinterface, business rule, and data layers, Oak Leaf enablesyou to create applications that promote the “dataanywhere” concept. The location of data (local or remote)can be specified globally, but can also be set at either thedata environment or cursor levels. If necessary, you caneven switch between local and remote data on the fly. Alldata access is through VFP views, and there doesn’tappear to be direct support (in the form of reusableclasses or documentation) provided for SQL Pass-Through (SPT). SPT data classes are available from othersources, though, and aren’t that difficult to create yourselfif you need them.

Not only can the data reside anywhere, it can also bemanipulated and returned in several different formats.Able to be set globally or at the business object level, theproperty allows a choice of VFP cursor, ADO, HTML, orXML data formats. After calling the SetDataFormat() orSetDefaultDataFormat() method with an appropriateparameter, subsequent calls to affected business objects’GetData() method return the associated data in thespecified format. This functionality is a necessity forcreating COM servers that can be used by non-VFP clientssuch as VB or ASP.

The combination of separating data access code fromthe user interface plus built-in functionality to serve updata in a variety of formats clearly demonstrates that MMis built with n-tier applications well in mind.

COM serversOnce you’ve followed Oak Leaf’s advice of putting themajority of your application’s code into business objects,

business rules, and event objects, you’re only a few mouseclicks away from creating COM servers. Thesecomponents can then be used, via Automation, byapplications written with any COM client, such as VisualBasic, Active Server Pages, and Microsoft Office. Youmight not believe it until you see it, so I suggest that youfollow the Developer Guide’s instructions for creating andtesting your first COM server. It takes only a few minutes,and I’ll be shocked if it doesn’t change your point of viewconcerning VFP, COM, and Internet applications. TheDeveloper Guide even contains detailed informationabout which framework files you must include in projectsused to build COM servers (.EXEs or .DLLs), so that youget the leanest server possible. It’s all spelled out andready to go for you, and I doubt there’s a simpler way tomigrate into COM and n-tier development.

Data entry forms and controlsNot all applications are Internet sites that employ COMservers to manipulate remote data sources—at least notyet. MM contains a base set of VFP form and userinterface control classes. As is typical, the vendor hasendowed these base classes with standard properties. Thebase form classes are devoid of any controls, freeingdevelopers to create forms with any desired look. Forthose of us who are in a hurry and are willing to acceptthe look of a form that we didn’t design, the frameworkalso contains several pre-built data entry form classescontaining the controls your users will need. Takeyour pick.

The beauty of this framework’s n-tier architecture isthat no data access code or business logic is expected toappear in your data entry forms. That stuff belongs in thebusiness objects and data access classes mentionedpreviously. This means that you’re free to create formsthat look and behave in whatever manner you wish. Youcan then drop your business objects onto any number ofthese forms, since those objects don’t care one bit abouttheir parent container.

MM also contains several unique data entry controlsthat you can drop on your forms and configure by settinga few properties. One is a date range entry control thatlets users quickly select ranges such as current month orquarter to date. Another is a double checkbox control thatmimics the behavior of controls found in the VFP viewdesigner. This feature allows you to specify which fieldsare keys and which are updateable.

Other formsFrameworks typically provide a number of pre-builtdialogs and forms that you’ll need in your applications,and MM is no exception. Many form classes are includedto support functionality, such as pick lists, movers, finddialogs, logins, report selection, and preferences selection.

One particularly interesting and unique form class is

http://www.pinpub.com 3FoxTalk Extended Article: April 2000

the sidebar form. This class allows you to create a desktopapplication that has the look of a Web browser app, with“links” to options along the left side of the display(Figure 1). The sidebar form contains code that registers itwith the application and adjusts the application’s settingsfor how wide the screen is. This intelligence allows formsto center themselves in the remaining desktop space.

Another very helpful implementation is theframework’s find dialog. When working with remotedata, you typically can’t create the ubiquitous VFP formwhere one page frame lists all of the data and additionalpages provide access to the selected record’s fields (butfear not, MM does include such a form class for use withlocal data). The MM find dialog class sports a query-by-example (QBE) interface and a grid for display andselection of matching entries (Figure 2).

ReportsMM contains some classes and an .FRX template that helpmake VFP reports seem somewhat object-oriented. Areport session class is responsible for setting up theenvironment for running a report. A report dataenvironment class is responsible for loading the cursorsthat the report needs. The .FRX template contains codethat creates and uses instances of these classes. The resultis a reporting environment that works well when reportdata can be supplied by views. Parameterized views andparameter input dialogs are also supported anddocumented. Of course, you’re also free to create yourown report classes or programs for those times when pre-defined views don’t work (or if you don’t want to create adifferent view for each of a hundred reports).

Component GalleryHave you used the Component Gallery tool that wasadded to VFP in Version 6? It includes the FoxProFoundation Classes (FFC) and also allows you to addcatalogs (folders) of your own class libraries. You thenhave easy drag-and-drop access to these classes, whichcan be organized in any way you desire (rather than onlyin rigid class libraries). Wouldn’t it be nice if frameworkvendors embraced this great new tool, so that you couldgain easier access to, for instance, all of the framework’sform classes, regardless of the library in which they’relocated? Wish no more, because MM ships with a tablesuitable for loading into the VFP Component Gallery andcreating a “Mere Mortals” catalog.

CollectionsMM provides a hierarchy of collection classes thatsupports both child and reference collections. If you’veworked with objects in VFP (or other languages), you’rebound to have come across collections. They’reeverywhere. For example, the VFP application (_Screen)object contains a collection of object references to theforms that you or the user currently has open. Formscontain a collection of controls. Page frames contain acollection of pages. There are many other collectionslurking in VFP.

Collections are mighty useful in your own object-oriented applications as well. For instance, you mightdefine a cursor as a collection of fields (ADO does). In arecent data import utility project, I created a user interfacecontrol that allows a user to preview the import data. Thatcontrol includes a collection of locations where the user

Figure 1. A sidebar form, demonstrated in the framework’ssample application.

Figure 2. A handy find dialog minimizes the amount of datatransferred from a remote source.

4 http://www.pinpub.comFoxTalk Extended Article: April 2000

clicked to indicate breaks in columns.There’s no end to the useful purposes you can find for

this construct. Each of them can be implemented bycreating a subclass of the MM collection classes, whichprovide methods for easily adding, accessing, andremoving child objects.

Library routinesMM includes a Utility.prg procedure file that containsover 100 library routines. These routines are welldocumented in comments, as well as in the DeveloperGuide. Procedures and functions are included for arrayhandling, file and directory management, data formatting,credit card validation, date/time manipulation, fonthandling, messaging, object querying, and wrappingWindows API functions.

Abstract factoryThe abstract factory design pattern promotes flexibility byallowing application components to be instantiatedwithout specifying their concrete class names untilruntime. An instance of MM’s abstract factory class isadded to the framework’s application object. Incombination with a lookup table, this feature allows youto build applications that decide how to behave atruntime, based on conditions that you dictate duringdevelopment. When creating an object with the abstractfactory, you pass a “token” (character string) to thefactory and ask it to create the appropriate object. Thefactory looks up the token in the application’s abstractfactory lookup table (where else?) and instantiates theindicated class. The MM Developer Guide and OO Guideboth do a great job of explaining this concept andshowing you how and why to use it.

Integration with other third-party productsMM includes component classes that integrate and wrapthe functionality of third-party products. Steve Black’sINTL Toolkit, Stonefield’s Database Toolkit, and TakeNote’s FoxAudit are specifically supported today, but thecomponent classes provide the framework for supportingother tools in the future. Of course, the popular FoxFire!and Stonefield Reports ad hoc reporting tools can beintegrated into your MM application with a minimumof fuss.

SecurityMM sports a robust security setup model, consisting of asecurity manager class, a security rules class, a usermanager class and security-aware user interface controls.The whole thing can be switched off as easily as settingone property on the application object. When a user logsin and is registered with the application by the usermanager, the security manager is summoned to determinethe access level that the user (and any groups to which theuser belongs) has to secured application elements. Thesecurity rules class is nicely separated from the securitymanager, allowing you to modify the security strategy inyour own subclass to support something other than thedefault full/read-only/no access levels. The default rulesstrategy also allows you to define whether full access orno access is assumed by default.

All UI controls in the framework contain a propertythat you can populate with a unique (within yourapplication) value. At runtime, the security manager usesthat value to determine the level of access appropriate forthe current user. In design mode, you may use theframework builder to generate this unique ID and enter adescription of the control. The description that you enterappears in the forms that allow you to grant access to thecontrol for specific users and groups (see Figures 3 and 4).

The framework also supports something called

Figure 3. You can set a control’s security information with theframework builder…

Figure 4. …and the user sees your description in the user andgroup maintenance forms.

http://www.pinpub.com 5FoxTalk Extended Article: April 2000

security mode. This feature allows permitted users tospecify user and group access rights for each securedcontrol by working directly with the forms that containthe controls. As shown in Figure 5, secured controls arehighlighted with a yellow border when the user is insecurity setup mode. Right clicking on a secured controlwhile in this mode launches the mover dialog boxpictured in Figure 6.

Example applicationThe MM example application demonstrates many featuresof the framework and provides a great tool for learning.Given the application’s theme (management of clients andprojects), most of us could probably benefit from using itas a starting point to create an application for managingour own projects and tasks. Be sure to check out theexample application’s feature that allows users to drag an

icon from a form and drop it on the desktop. The user canlater return to the selected record in that form by double-clicking the nifty “desktop shortcut” (Figure 7).

Learning curveDepending upon your level of experience with andacceptance of OOP and COM principals, you might be infor quite a ride. Building a completely object-oriented, n-tier application is massively different from creatingmonolithic monstrosities where much of the code lives incommand buttons within forms. Mounds of evidencesupport the notion that n-tier architecture is beneficial formost of our work (when’s the last time you had arequirements-gathering meeting where someone didn’tthrow in, “Oh, and we’re going to want to access this overthe Internet too” toward the end?).

So, if you’re like most of us, I expect that you’ll needsome help up the learning curve. Luckily, the MMdocumentation is very informative and easy to follow. Asyou learn more about OOP, COM, n-tier architecture,UML, and the like, you’ll probably want to obtain andread some of the books that Kevin McNeish recommends.If your budget allows, you might also wish to attend oneof Oak Leaf’s classes. However you get there, once youreach the point of understanding and embracing the n-tierarchitecture, I predict you’ll never want to go back. Inother words, this is one learning curve that you shouldfind more than worth the trouble.

DocumentationThe MM documentation is extensive and extremely welldone. You’ll probably never run into a framework featurethat isn’t documented, because the folks at Oak Leaf don’tseem to let that happen. Often, update versions are heldback from release for a few days or weeks for the solepurpose of ensuring that the documentation is properlyupdated first.

Figure 5. Secured controls are highlighted when the user selectsSecurity Setup mode.

Figure 6. Right-clicking a secured control invokes this moverdialog box.

Figure 7. The example application demonstrates frameworkfeatures, such as “desktop shortcuts.”

6 http://www.pinpub.comFoxTalk Extended Article: April 2000

The primary documentation is a set of MicrosoftWord documents that includes a Jump Start Guide withinstallation and tutorial information, a What’s NewGuide, a unique Guide to Object-Oriented Design in VFP,and a 300+ page Developer’s Guide. The Developer ’sGuide contains liberal doses of helpful screen shots andUML diagrams. Several UML model files are installedwith the framework, providing the schematics for theframework object model, ADO, and ASP. If you knowUML, the included diagrams will assist you in your questto learn about these object models. If you don’t knowUML, the documentation will help you learn that as well.

Written documentation is important, of course, butcode comments are at least equally important when itcomes to understanding how things work. Standardheader and modification comment blocks appear at thetop of nearly all framework methods and procedures, andinline comments are plentiful. Most classes and theirPEMs contain comments that appear in the propertywindow, project manager and class browser; however,there are a quite a few classes and PEMs that don’tcontain descriptions in these comments. True to itsCodebook roots, MM encourages developers to includethe same level of comments in their own code. This isaccomplished by adding to the development environmenta menu pad that contains numerous options for pastingformatted comment blocks into your code.

Development methodologyMM, through documentation and examples, stronglyencourages developers to build flexible, n-tierapplications. Even if the applications that you developwill never grow beyond single-user, desktop apps that useonly FoxPro data, you and your users would benefitgreatly from n-tier architecture. Through this architecture,MM promotes reusability and flexibility that just don’texist in monolithic implementations. If object-orientedtechniques and Microsoft DNA are new to you, theframework documentation (and the books recommendedwithin it) will help you embrace these vital technologies.

Properly designed, MM-based applications areconstructed from small classes that do one thing or a setof related tasks (isn’t this what OOP is supposed to be allabout?). I’m sure that it’s possible to create .SCX formsbased on MM form classes and then load up those formswith code. You won’t find any information orencouragement from Oak Leaf in this endeavor, however.So, to “be one” with this framework, you’ll need to learnand embrace the basic and beneficial principals of OOP. Ifyou already work from that mindset, the MM objectmodel should help you remember not to cut corners(by placing business logic in form control events,for example).

Development cycleIf, like me, you’ve grown accustomed to using .SCX formsand loading them up with code, using MM might forceyou to change your development cycle slightly. Thebenefit of creating monolithic VFP forms is that you canbe incredibly productive. Merely create a form based on aclass that includes common data-entry buttons, drag anddrop fields from a table in the data environment, then addform-specific code and controls, and you can be done witha standard data entry form in minutes. Testing andtweaking these forms is a dream, thanks to VFP’s toolbarbuttons that let you quickly cycle between running anddesigning a form.

While convenient for forms that you write once andnever maintain (do you have any of those?), takingadvantage of the development cycle I described abovewill come back to haunt you when it’s time to maintainyour application. Forms constructed as I described above,while initially inexpensive and fun to build, typically lackflexibility and are difficult to maintain. They cost more inthe long run because you end up having to make massmodifications to accomplish tasks such as changingbusiness rules or moving your data to SQL Server orthe like.

Because MM encourages you to build form classesthat are assembled from smaller components themselves,and because some of these components rely upon servicesthat are provided by other framework classes, you’ll begreeted with errors if you attempt to test a data entryform on its own. You must instead run the applicationthrough its main program. Although this requirementmight add seconds or minutes here and there to the timeit takes you to develop and test your forms, the flexiblearchitecture will probably save you hours when it’s timeto alter a form’s design or behavior. A side benefit to thislonger development cycle might be that, since it’ll be lessconvenient to make a small change and immediately testit, you’ll better plan the changes that you intend to makeand how you’ll test them.

If you’ve used Codebook or FoxExpress for years, orif you’ve already been creating form classes rather thanthe .SCX variety, you probably wonder why I made such abig deal in this section. I suspect, however, that themajority of VFP developers use .SCX forms as a crutch,mostly for the reasons I’ve detailed above. If so, perhapsthis section will cause many of us to reconsider thatchoice. Using MM certainly will.

Vendor/principalsKevin and Nicole McNeish run Oak Leaf Enterprises.They should be familiar to anyone who’s attended aFoxPro developer conference. Kevin’s presentations atthose conferences are always among the best and most

http://www.pinpub.com 7FoxTalk Extended Article: April 2000

thought-provoking, in my opinion. His presentations andmagazine articles always seem to be written with the goalin mind of helping us (especially those of us who continueto think procedurally) experience the “aha” moments thatmust occur when shifting to the OO paradigm. Nicolecontributes to the product’s superb writtendocumentation and also provides technical support.

Some developers might be concerned about relyingupon such a small company to supply the support andupdates that we all expect and need when buildingapplications with a framework. True, the pace of newfeatures probably is somewhat slower than it could be ifOak Leaf were a company with dozens or hundreds ofdevelopers, but new features and updates do continue toroll out at a decent pace. Many new features are theresults of requests from Oak Leaf’s customers. Theversions that are released seem remarkably stable, givingme some confidence that Kevin probably spends moretime using and enhancing the framework than he doesfielding support calls and hurriedly producing andshipping bug fixes.

PerformanceAs I mentioned in my previous article about how toevaluate frameworks (“Evaluating ApplicationFrameworks” in the October 1999 issue of FoxTalk), nearlyall application frameworks must contain a certainamount—perhaps lots—of “generic” code to handlevarious situations. This additional code and flexibility

Because this framework’s business objects areresponsible for deciding whether to use a local or remoteview at runtime, this arrangement, technically, doesn’tviolate the n-tier principle of separating data access fromuser interface controls. While some might argue that MMis not truly an n-tier application framework because ofthis “transgression,” I’d argue that this design doesn’tlimit the flexibility that an n-tier design provides. Giventhat no flexibility is sacrificed, the impact that this designhas on performance makes a believer out of me.

Source codeAn MM license comes with complete source code,including the code for all builders and other developertools. An examination of the code shows that the vendorreligiously follows an important object-orientedprinciple—“write short methods that do one thing well.”In fact, most methods contain more lines of commentsthan lines of code.

As always, there will be times when you’ll set aproperty incorrectly or pass an invalid parameter to aframework method. One thing that might take somegetting used to in those cases is that the code is nearlyalways suspended within an Error method on the line thatcauses the error to be displayed. That means that youneed to become familiar with the call stack window of thedebugger so that you can determine which methodactually encountered the error. If you need to debug the

Figure 8. The VCXEdit form lets you “hack” class libraries when the need arises.

almost has to result in applicationsthat run slower than ones for whichyou handcraft each line. So, for thepurposes of our evaluations, we seekonly to determine whether eachparticular framework performs aboutthe same as the others, much fasterthan the others, or much slower thanthe others.

I’m pleased to report thatapplications created with MM seemquite a bit snappier than ones createdwith other frameworks that I’veevaluated. When asked about this,Kevin McNeish replied, “Everythingadded to the framework isperformance tested before you’ll eversee it in a release product. This is, forexample, why data controls in MMcontinue to be bound to view fieldsrather than directly to business objectproperties. With the current VFPversion, binding to object propertiesincurs significant performancepenalties over binding to table orview fields.”

8 http://www.pinpub.comFoxTalk Extended Article: April 2000

situation, you’ll often need to set a breakpoint in themethod after you’ve located it, then perform your testagain. This is the price to pay for using a well-designederror handler (which will be familiar to anyone who hasattended an error handling session presented by DougHennig or read one of his FoxTalk articles on the subject).

Included developer toolsMM includes a growing number of builders that helpmake developers more productive. Recent additionsinclude a builder that allows you to select an object’scontrolsource from a dialog box listing the fields for alltables and views in your application’s metadata.Considering that the framework eschews use of VFP’snative data environments in favor of more flexible customdata environment classes, this new builder is a verywelcome addition. Other builders are included to helpyou create business objects and data environments. Onecan hope that, someday, a builder will be included to helpwith creating the framework’s object-oriented,programmatic menu classes.

Another tool that you’ll probably find quite handy isthe VCX Editor (Figure 8), which allows you to makechanges directly to class libraries.

SupportFree support for MM is provided for 30 days followingthe first question that you e-mail [email protected]. Following that, you may continueto ask technical questions via e-mail at a cost of $20 perquestion. Unlimited free peer support is available in theThird-Party Products section of The Universal Thread(http://www.universalthread.com). At times, Oak Leafstaff members respond to questions posted there. You’realso likely to find peer support available in just about anyof the other online FoxPro communities.

TrainingOak Leaf offers two hands-on courses in Charlottesville,Virginia and other locations. The first course is a three-day immersion in the framework, while the other is atwo-day, team-oriented workshop that teaches object-oriented analysis and design principles. Both classes aretypically taught during the same week. The price ofattendance for all five days is about $2,000 (withdiscounts for multiple enrollments).

I haven’t personally attended one of these classes, butI have been fortunate enough to spend one day with

Kevin in a pre-conference session as well as an eveningwhen he flew into town to speak to the local user group.Those brief experiences, combined with the praise forthese classes that I’ve seen in online discussions, make myfuture attendance a certainty. I suggest that you visit thevendor’s Web site for additional course details and, if youhave the opportunity to attend one of Kevin’s pre- orpost-conference sessions, I recommend that you seize it.

CostThe Mere Mortals Framework 6.0 costs $399 perdeveloper. There are no runtime royalties, but any clientwho wants a copy of the source code must also purchase alicense (or one per developer). Version upgrades forcurrent customers are available at a reduced price. Minorversion number upgrades typically are made available tolicensees at no cost.

SummaryIf you’re convinced that the Windows DNA architecture isthe way to go and you’re struggling with just how todesign and build n-tier VFP applications, The MereMortals Framework is probably just what you need. Evenif you plan to build your own custom framework forinternal use, the OO designs and ideas documented andimplemented by Oak Leaf are sure to be worth the priceof admission. ▲

A senior developer, project manager, and trainer with Vision Data

Solutions in Independence, MO, Kelly has worked with every version of

FoxPro since 2.0. He’s a Microsoft Certified Solution Developer and

teaches developer classes in Visual FoxPro, SQL Server, and Visual Basic.

[email protected].

The Mere MortalsFramework 6.0

Oak Leaf Enterprises Solution Design, Inc.

952 Rockledge Drive

Charlottesville, VA 22903

804-979-2417

http://www.oakleafsd.com

$399 per developer

http://www.pinpub.com 1FoxTalk Extended Article: April 2000

FoxTalkSolutions for Microsoft® FoxPro® and Visual FoxPro® Developers

This is an exclusive supplement forFoxTalk subscribers. For more

information about FoxTalk, call us at1-800-788-1900 or visit our Web

site at www.pinpub .com/foxtalk.

Extended Article

Cool Tool: A New Spin onThermometer BarsWhil Hentzen

FoxTalk editor Whil Hentzen continues his Cool Tool serieswith a showcase of Todd Sherman’s spiral thermometer class.

WE’VE all seen thermometer bars of variousshapes and sizes, and they all have two thingsin common: They all take a parameter whose

value changes as a process progresses, and they alldemonstrate progress by displaying a horizontal (or,occasionally, vertical) bar advancing across the screen in ahorizontal window.

How last century.Todd Sherman of Compass Resources put together a

class that performs the same function. Instead ofdisplaying a horizontal bar, though, the visual appearancemimics the aperture of a camera lens, starting with a dotin the center of the thermometer bar form and openinginto a circle whose diameter is just a hair narrower thanthe width (or height) of the containing form.

Yeah, that’s a lot of words. How about some pictures,such as Figure 1, Figure 2, and Figure 3?

The class consists of a few properties and a methodthat draws a line—as you’ll see if you examine the figures,the spiral is really just a series of lines drawn around acentral point. The line drawing method is called with aparameter that you can use to mark the progress of theprocess you’re running. Here’s the guts of the testprogram Todd provides to show how to call the class.

set proc to spiral_thermmax_x = 1000oMeter = createobject("spiral_therm")oMeter.showfor x= 1 to max_x - 1 oMeter.SetSize(x/max_x)next

The class itself is a bit more involved, and you mightwant to get out your high-school trig book if you want to

hack the code at all:

* see test_spiral.prg for usagedefine class spiral_therm as formautocenter = .t.width = 300height = 300caption = 'Progress Meter'backcolor = rgb(255, 255, 255)name = 'form1'* properties for the spiral itselfradius = 0rot_progress = 500000rot_step = 57radius_inc = .1

Figure 1. Todd Sherman’s spiral thermometer starts with a dot inthe center of the form.

6.06.0

2 http://www.pinpub.comFoxTalk Extended Article: April 2000

Figure 3. When processing is complete, the spiral thermometerdisplays a spiral that fills the form. It looks even better in person,because the spiral is drawn in multiple colors.

Figure 2. Todd Sherman’s spiral thermometer about one-third ofthe way through a process.

* this method is the guts of the classprocedure setsize parameter sizemult * sizemult goes from 0.001 to .999 * do while the radius of the circle being drawn is < * half the width of the form * the multiplier do while thisform.radius < thisform.width/2 * sizemult * adjust the values that make up the color of * the line * 0 to 255, 255 to 0, bell curve 0 to 63.75 to 0 goodblue = ; 0+(255*(thisform.radius/(thisform.width/2))) goodred = ; 255-(255*(thisform.radius/(thisform.width/2))) goodgreen = ; (thisform.radius/(thisform.width/2))*goodred thisform.forecolor=rgb(goodred, goodgreen, goodblue) * rotation progress goes from 0 to 85,500 thisform.rot_progress = thisform.rot_progress ; + thisform.rot_step * create the start and end points of each line * being drawn around the circle startx = sin(thisform.rot_progress) * thisform.radius ; + thisform.width/2 starty = cos(thisform.rot_progress) * thisform.radius ; + thisform.width/2 endx = sin(thisform.rot_progress + thisform.rot_step) ; * thisform.radius + thisform.width/2 endy = cos(thisform.rot_progress + thisform.rot_step) ; * thisform.radius + thisform.width/2 thisform.line(startx, starty, endx, endy) thisform.radius = thisform.radius + thisform.radius_inc enddo if sizemult = 1 then thisform.release endifendprocenddefine

define class spirallabel as label extracaption = "Complete"enddefine

As with many of the Cool Tools featured in FoxTalk,this one is provided with source code, but “as is”—youmight want to modify or enhance it further. I can think ofa number of features you might want to tweak.

First, you might want to remove the title bar of theform and change the backcolor of the form to match theapplication you’re working on. If you’re mathematicallyinclined, you might try tweaking the code so that the classdraws from the outside to the center, giving theimpression of the aperture lens closing—particularlyappropriate if the process you’re metering signifies thetermination of an event.

If you want to bother Todd, or tell him thanks, orsend him a copy of your enhancements, or send him a boxof Frangos, his e-mail is Todd Sherman([email protected]). Ummm, on second thought,forget what I said about bothering him, okay? ▲

04COOLSC.ZIP at www.pinpub.com/foxtalk

Whil Hentzen is the editor of FoxTalk.

http://www.pinpub.com 1FoxTalk Extended Article: April 2000

FoxTalkSolutions for Microsoft® FoxPro® and Visual FoxPro® Developers

This is an exclusive supplement forFoxTalk subscribers. For more

information about FoxTalk, call us at1-800-788-1900 or visit our Web

site at www.pinpub .com/foxtalk.

Extended Article

Third Party Tool Forum:Controlling User Access toApplication Data in Foxfire!Bill Wood 6.06.0

Controlling user access to application data is an importantpart of providing a managed query and reportingenvironment. For example, when setting up a querying andreporting environment for users of an accounting application,you might want to restrict querying of payroll data on a user-by-user basis. Bill Wood explains how Foxfire!’s data-drivendesign makes it easy for you to implement this type ofsecurity as well as a wide variety of other security schemes.

EACH application you create will probably have adifferent set of querying and reporting data accesssecurity requirements. Foxfire!’s security

architecture is designed for maximum flexibility to enableyou to create your own custom security scheme tailoredto each application. The flexibility to create a tailoredsecurity scheme is made possible using a multi-layeredapproach. The four layers in the Foxfire! data accesssecurity architecture are:

1. User ID Authentication and User Security Level2. Filtering Foxfire! System Tables3. User Privileges4. Request Ownership and Limits on Request Editing

Each successive layer gives you finer control overuser access to data, and you can target specific layers toan individual user or groups of users.

User ID authentication and user security levelThe first layer of security involves authenticating the userID and establishing the user’s security level. Foxfire! v6.0now supports user ID and password authentication tocontrol access to the application. In previous versions of

Figure 1. Foxfire! v6.0 now provides a built-in log-in dialog box togive you the option to prompt users for an ID and password.

the product, user ID needed to be established externallyand then passed in as a parameter, and there was noauthentication of the ID. In v6.0, user ID and passwordare authenticated against existing records in the UserTable (the User Table is also a new addition to v6.0 and isset up and maintained using the User Account Manager).You can now either pass in the user ID or prompt the userfor an ID and password using the built-in log-in dialogbox (see Figure 1). Either way, the user ID must beauthenticated before the application will be allowed tostart up. Examples of this technique follow.

Example 1Passing in the user ID using command line parameters:

DO FOXFIRE WITH "REQUESTS","JOHN","","FFCONFIG"

2 http://www.pinpub.comFoxTalk Extended Article: April 2000

Example 2Forcing a user log-in dialog box to prompt the user byleaving user ID empty:

DO FOXFIRE WITH "REQUESTS","","","FFCONFIG"

You might wonder why there’s no passwordparameter when passing in the user ID. Foxfire! assumesthat if you’re passing in a user ID, you’ve alreadyprompted the user for user ID and password informationsomewhere else. Therefore primary security has alreadybeen established and you need only to establish the userID to determine user security level and facilitate the otherthree security levels. Also, specifying a password in thecode could compromise security.

Once the user ID has been authenticated and the userestablished, the contents of the user record are scatteredinto memory variables which will stay in scopethroughout the Foxfire! session and are referred to as theuser variables (all begin with m.LG_).

Two of these variables, the user ID (m.LG_USERID)and the System Administrator flag (m.LG_ADMIN), canbe used in conjunction to provide many means ofcontrolling access to your application data. Each newFoxfire! request will have it’s ownership established usingm.LG_USERID, and the value of m.LG_ADMIN is used toestablish the user’s security level.

In previous Foxfire! versions, security level wascontrolled by the variable FF_SECLVL. This variable isstill used for this purpose to maintain backwardcompatibility, but it’s now automatically set depending onthe value of m.LG_ADMIN. FF_SECLVL has only twostates, either “*” or not “*”. When the value is “*,”Security Level is set to “Developer,” and the user will beimmune to any system privilege settings (see“Privileges”). When the value is not “*,” security level isset to “Non-Developer,” and system privileges settingswill be in effect. In previous versions of Foxfire!, you hadto set FF_SECLVL programmatically in the Foxfire!configuration file FFCONFIG.PRG. If you wanted tocontrol Security Level on a user-by-user basis, you had towrite logic in one of the hooks in FFCONFIG. Now, thevalue of FF_SECLVL is set to “*” if m.LG_ADMIN = .T.,and it is set to “USER” if m.LG_ADMIN = .F.

Note: All of the new user ID authentication featuresare optional in the Standard Edition. Passing in the userID without authentication is still supported in order tomaintain backward compatibility to previous versions.However, Enterprise Edition requires the mandatory useof user ID authentication to control end-user licensing.

Filtering Foxfire! system tablesFoxfire!’s user interface is heavily driven by the contentsof tables, referred to as Foxfire! system tables or Foxfire!metadata. If you control access to the records in these

tables, you effectively control access to what a user cansee and do with the Foxfire! application. Because thesystem tables are FoxPro tables, the strategy here is toapply filters to these tables in order to control user accessto specific system table records.

The three tables of primary interest are the Preferencetable, the Request table, and the Data Items table (thedata dictionary).

The Preference tableThe Preference table is the highest-level organizationconstruct in Foxfire!. Each Preference record maintains areference to a Request table and a Data Items table. Youcan create multiple preferences from which users canchoose. These preferences either partition a singledatabase or provide querying and reporting for multipledatabases (see Figure 2).

The tables and fields that can be queried, as well as allexisting requests, are therefore indirectly controlled by thepreference. If you have multiple preferences, and want toprevent certain users from accessing the requests and dataitems associated with certain preferences, you simplyapply a filter to the Preference table.

The best place to apply filters to the Preference tableis in the Foxfire! Preference Setup phase programminghook. Located in the Foxfire! configuration fileFFCONFIG.PRG, this phase is called when Foxfire! startsup and attempts to establish the active preference. ThePreference table is open and is the current alias at thispoint in Foxfire! execution.

Example 3For example, the Preference table contains a “department”field, PF_DEPT. The user record also contains adepartment field, LG_DEPT. I want to allow users toaccess only those preferences for which the user’sdepartment matches the Preferences department. Also, I

Figure 2. The Preference Set Picker displays all availablePreferences available to the user.

http://www.pinpub.com 3FoxTalk Extended Article: April 2000

want to exclude users with “Developer” security levelfrom this constraint.

In the Preference Setup phase in FFCONFIG.PRG,you’d enter the following code:

IF !m.lg_admin SET FILTER TO ALLTRIM(pf_dept) = ALLTRIM(m.lg_dept)ENDIF

The Request tableAfter a startup preference has been established in thePreference Setup phase, the associated Request table forthat preference is opened to fill the list of availableRequests displayed in the Request Manager (see Figure 3).Each item in the Request list is a record in theRequest table.

To prevent users from running particular requests,you can filter the Request table so that these requestswon’t appear in the Request list. In the Preference SetEditor | Files dialog box, you can specify a filterexpression to be applied for each of the system tables in apreference (see Figure 4).

In this diagram, you can see that each system tablehas an associated text box for entering a filter expression.To filter the Request table, you can enter any valid FoxProfilter expression in the “Filter” text box. The expressionentered for the Request table is saved in the preferencefield PF_FLTREQ. Whenever the Request table is opened,Foxfire! checks to see if the field PF_FLTREQ contains anexpression, and if so, attempts to apply the filter to thetable.

Note: No validation is attempted on this expression.If you enter an invalid expression, Foxfire! will generatean error.

Example 4For instance, when a user creates a Foxfire! request, therequest is tagged with the user ID of the user who createdthe request (using m.LG_USERID). The value in

m.LG_USERID is stored in the Request table fieldRQ_USER. If you wanted to filter the Request table so thatusers can see only requests that they created, you’d enterthe following filter expression:

Rq_user = UPPER(ALLTRIM(m.lg_userid))

Using filter expressions to control access to recordsgives you a great deal of flexibility when implementing asecurity scheme.

Example 5Example 4 used Rq_User in the filter expression, but youmight want to filter on a wider range of user subsets. Toachieve this, you’ll need more variables in the filter. Avariable that is often used for filtering purposes is theRequest field RQ_MISC. This field is a miscellaneous fieldnot used by the Foxfire! application, and is available forany purpose. A common use for this field is to storeadditional information about a request for use in filteringthe Request table.

What if you wanted users to see their own requests orany requests that you’ve tagged as being for “General”use? You could browse the Request table and place thevalue “General” in appropriate Request records. Then,change the Request filter expression to:

Rq_user = UPPER(ALLTRIM(m.lg_userid)) ; OR Rq_misc = "General"

Note: You can add fields to any Foxfire! system tablewithout damaging the Foxfire! application. Add as manyfields for filtering as you need.

The Data Items tableThe Data Items table holds all the table, field, and customdata item information for a preference (this is alsoreferred to as the Foxfire! data dictionary). The techniquefor controlling access to individual data items is exactly

Figure 3. The Foxfire! Request Manager shows the list ofrequests, which is driven from records in the active Request table.

Figure 4. The Files dialog box in the Preference Set Editor displaysthe name and location of each system table.

4 http://www.pinpub.comFoxTalk Extended Article: April 2000

the same as with the Request table.One small difference is that a miscellaneous field

(RDI_MISC) in the Data Items table is surfaced in the DataItem Editor interface. In the Advance Options dialog boxthere is a text box for entering “User-Definable GroupIDs” (see Figure 5). Values entered in this text box aresaved in the field RDI_MISC.

Example 6You could create a Group ID scheme such as: “A” = All;“P” = Payroll. Then you could enter a filter expression forthe Data Items table in the Preference | Files dialog boxsuch as:

"A" $ rdi_misc OR ("P" $ rdi_misc ; AND m.lg_dept = "Payroll")

This filter would ensure that data items tagged with

“A” are available to all users, but data items tagged with“P” would be available only to users who belonged to thepayroll department.

User privilegesFoxfire! uses a privilege system to control a user’s abilityto add, edit, or delete requests and data items. Privilegesare actually used to control access to every aspect of theFoxfire! interface, but I’ll focus on requests and dataitems. One clear way to prevent users from accessingsensitive application data is to simply prevent them frombeing able to add or edit requests.

The Preference table maintains the privileges foradding, editing, and deleting requests. You set privilegesusing the Privileges dialog box in the Preference SetEditor (see Figure 6).

The Request Manager Privileges in the upper left-

Figure 5. The Advanced options dialogbox in the Data Item Editor allows you totag each data item with a Group ID, whichcan then be used for filtering the DataItems table.

Figure 6. The Privileges dialog box in thePreference Set Editor is where you setsystem-wide privileges for users with”Non-Developer“ security level.

http://www.pinpub.com 5FoxTalk Extended Article: April 2000

hand portion of the dialog box dictate whether a user cancreate, edit, or delete requests. If you “uncheck” theseoptions, users that don’t have “Developer” security levelwon’t have the ability to add, edit, or delete requests.They can only run or preview preexisting requests.

Request ownership and limits on request editingAlthough it’s possible to prevent a user from editing anyrequests at all by setting an appropriate privilege, anotheroption is to “lock down” one or more portions of arequest, thereby preventing them from being edited byusers other than the request’s owner. As described above,the owner of a request is the user who created it andwhose user ID is saved as the value of the RQ_USER field.If an owner wants to protect all or a portion of therequest, he or she may use the locking options in the“Save As” feature of the Request Editor (see Figure 7).

Each Request Editor subdialog may be locked by theowner of the request so that other users can’t modify thatportion of the request. Locking involves choosing theappropriate lock options in the Save As dialog box, thensaving the request. If the new request name is the same asthe old one, the net effect is to change the locking status ofthe request without creating a copied version of therequest. Locking all four parts results in a completely

“read only” request when edited by anyone other thanthe owner.

Locking portions of a request can assure that vitalparts of a request, which could affect access to applicationdata if changed, won’t be modified, while still allowing auser to vary a query as needed. For example, you mightwant to lock user access to the Data Item Picker, but stillallow users to change Filter criteria, thus strictlycontrolling which data items they can see. Or, you couldlock user access to the Filter Builder, but give them accessto the Data Item Picker, thus strictly controlling thenumber of records returned by the query.

Note: Request-level locks are completely ignoredunder two circumstances: when the user is a SystemAdministrator or when the user is already the owner ofthe request.

I hope this outline of the four main layers of Foxfire!’ssecurity architecture gets you started towardimplementing your own security requirements. Foxfire!’sopen architecture should make it possible to create avariety of security schemes to control access to yourapplication data. ▲

Bill Wood is the Foxfire! Program Manager at Micromega Systems, Inc. in

Corte Madera, CA. [email protected].

Figure 7. The “Save As” dialog box in the Request Editor lets you lock portions of a request.