FoxTalk - Deutschsprachige FoxPro User...

24
1 Instrumenting FoxPro Applications Rod Paddock 2 Editorial: A Modest Proposal— The Sequel Whil Hentzen 5 Incorporating MS Info into VFP Applications Richard A. Schummer 8 Keep Your Effective Conventions Steven Black 10 Extending VFP Through the API: Calling All Windows Gary DeWitt 11 Best Practices: Use Version Control for Added Safety Jefferey A. Donnici 15 Reusable Tools: It Just Kicks in Doug Hennig 19 ActiveX and OLE Automation Review: The Progress Meter ActiveX Control John V. Petersen 22 The Kit Box: Kit Box Potpourri Barbara Peisch 23 The Cutting Edge: The Texas Heart Les Pinter 24 September Subscriber Downloads September 1997 Volume 9, Number 9 Fox Talk Continues on page 3 Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers Instrumenting FoxPro Applications Rod Paddock You’ve spent days, weeks, months, or years putting together the perfect application—but what do your users think? Specifically, are they making full use of it, or are there pieces that are underused, or even unused? In this article, Rod discusses a technique to measure which pieces of your application are being used and to what extent. I T’S always fun how these articles come together. I was sitting in the Toronto airport with Whil Hentzen talking about—you guessed it—FoxPro development. During this discussion, we came to the topic of navigation buttons and whether or not users actually use them. It was my contention that users don’t use navigation buttons because they’re not a very convenient method of finding any real information. Whil wasn’t so sure I was right. So I asked, “Have you ever instrumented your applications to find out?” Whil said he had used a similar technique for testing, but not in a production application to track the actual usage of an application. I stated that it’s important to instru- ment applications to see just what people are actually doing with them. Subse- quently, Whil asked me to write the article you’re holding in your hands now. Software developers have been faced with the problems of feature bloat for years (a.k.a. Rampant Featuritus). In my first MIS job, I was tasked with the job of producing the daily, weekly, and monthly reports that were used to run our company. There were literally dozens of reports to be run on a periodic basis. After a time, I began to question the need for so many reports and whether people were actually reading them. So I decided to conduct a little experiment—I stopped creating and sending the reports. Now guess what happened: Only a few people asked for the reports. Through this rather unscientific study, I was able to trim down the number of reports being created by more than half. Why did this work? Because people who needed the reports “screamed,” but everyone else remained quiet. Why were so many reports abandoned? Mostly because business needs had changed. Information that was once useful was no longer so valuable. Eventually the job of running

Transcript of FoxTalk - Deutschsprachige FoxPro User...

Page 1: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

1 Instrumenting FoxProApplicationsRod Paddock

2 Editorial: A Modest Proposal—The SequelWhil Hentzen

5 Incorporating MS Info into VFPApplicationsRichard A. Schummer

8 Keep Your EffectiveConventionsSteven Black

10 Extending VFP Through the API:Calling All WindowsGary DeWitt

11 Best Practices: Use VersionControl for Added SafetyJefferey A. Donnici

15 Reusable Tools: It Just Kicks inDoug Hennig

19 ActiveX and OLE AutomationReview: The Progress MeterActiveX ControlJohn V. Petersen

22 The Kit Box: Kit Box PotpourriBarbara Peisch

23 The Cutting Edge:The Texas HeartLes Pinter

24 September SubscriberDownloads

September 1997Volume 9, Number 9

FoxTalk

Continues on page 3

Solutions for Microsoft® FoxPro® and Visual FoxPro® Developers

InstrumentingFoxPro ApplicationsRod Paddock

You’ve spent days, weeks, months, or years putting together the perfectapplication—but what do your users think? Specifically, are they making full use ofit, or are there pieces that are underused, or even unused? In this article, Roddiscusses a technique to measure which pieces of your application are being usedand to what extent.

IT’S always fun how these articles come together. I was sitting in the Torontoairport with Whil Hentzen talking about—you guessed it—FoxProdevelopment. During this discussion, we came to the topic of navigation

buttons and whether or not users actually use them. It was my contention thatusers don’t use navigation buttons because they’re not a very convenientmethod of finding any real information. Whil wasn’t so sure I was right. So Iasked, “Have you ever instrumented your applications to find out?” Whil saidhe had used a similar technique for testing, but not in a production applicationto track the actual usage of an application. I stated that it’s important to instru-ment applications to see just what people are actually doing with them. Subse-quently, Whil asked me to write the article you’re holding in your hands now.

Software developers have been faced with the problems of feature bloatfor years (a.k.a. Rampant Featuritus). In my first MIS job, I was tasked withthe job of producing the daily, weekly, and monthly reports that were used torun our company. There were literally dozens of reports to be run on aperiodic basis. After a time, I began to question the need for so many reportsand whether people were actually reading them. So I decided to conduct alittle experiment—I stopped creating and sending the reports. Now guesswhat happened: Only a few people asked for the reports. Through this ratherunscientific study, I was able to trim down the number of reports being createdby more than half. Why did this work? Because people who needed the reports“screamed,” but everyone else remained quiet. Why were so many reportsabandoned? Mostly because business needs had changed. Information thatwas once useful was no longer so valuable. Eventually the job of running

Page 2: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

From the Editor FoxTalk

2 http://www.pinpub.com

Editorial Hotline770-565-1763800-788-1900

[email protected]

Pinnacle Web Sitehttp://www.pinpub.com

Fax770-565-8232

MailPO Box 72255Marietta, GA30007-2255

FoxTalk (ISSN 1042-6302) is published monthly (twelve times peryear) by Pinnacle Publishing, Inc., 1503 Johnson Ferry Road, Suite100, Marietta, GA 30062. The subscription price of domesticsubscriptions is: 12 issues, $179; 24 issues, $259. Periodical postagepaid at Marietta, GA and at additional mailing offices. USPS#005373.POSTMASTER: Send address changes to FoxTalk, PO Box 72255,Marietta, GA 30007-2255.

Copyright © 1997 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. Thispublication 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 particular

purpose. 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.

Subscription information: To order, call Pinnacle Customer Serviceat 800-788-1900, or 770-565-1763. Cost of domestic subscriptions:12 issues, $179; 24 issues, $259. Canada: 12 issues, $194; 24 issues,$289. Outside North America: 12 issues, $199; 24 issues, $299.Individual issues cost $17.50 ($20 in Canada, $22.50 outside NorthAmerica). All funds must be in U.S. currency.

For European newsletter orders, contact: Tomalin Associates,Unit 22, The Bardfield Centre, Braintree Road, Great Bardfield, EssexCM7 4SL, United Kingdom. E-mail: [email protected]: +44 (0)1371 811299. Fax: +44 (0)1371 811283. 12 issues: £179

For Australian newsletter orders, contact: 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]

FoxPro technical support: Call Microsoft at 206-635-7191(Windows) or 206-635-7192 (Macintosh).

Send all other questions or requests via the options at right.

Editor Whil Hentzen, Senior Editor Roland Winkler, Associate Editor Jeana Frazier, Production Editor Paul Gould,Editorial Advisory Board Scott Malinowski, Walter Loughney, Les Pinter, Ken Levy, Managing Editor Heidi Frost,Publisher Robert Williford, Vice President/General Manager Connie Austin

Applies to FoxPro v2.x

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

Applies to VFP v3.0

Applies specifically to one of these platforms.

Applies to VFP v5.0

Continues on page 6

A Modest Proposal—The SequelWhil Hentzen

NO, I’m not going to suggest you start eating yourchildren. Yet.Let’s suppose you wander into the offices of

Blackstone in Boston, Flash Creative Management in NewJersey, RDI or IMG in Chicago, The Power Store in SanFrancisco, a certain custom shop in Milwaukee, or, oh heck,virtually any firm involved in software developmentanywhere else in the country. Take a few principals tolunch, and have them spill their guts to you: “What’s yourmost pressing problem for the next six months?”

You’ll hear the same thing from each person you talkto: The difficulties in finding qualified technical talent. Andit’s getting worse. A recent estimate by Steven Levy at theCenter for Continuing Study for the California Economyestimates that the unemployment rate for technicalspecialties is less than 1%. For those of you who sleptthrough Econ 201, this is merely frictional unemployment—folks who have quit their Chips R Us job at noon and arestarting with We Be Processors after lunch.

My shop has been scared to answer the phone formonths—it might be a potential customer with more work.

We’d like to do more projects, but where do we find thepeople? Here are the available scenarios:

• Hire experienced people. Well, this is pretty much alosing scenario. There simply aren’t that manyexperienced (and competent) people around. Thosewho are around are really expensive. (And, frankly,there are a lot of expensive people out there whoaren’t at all experienced.)

• Hire newbies at a lower rate and train them. Lots ofdanger here as well. First, while the cash out of thedoor each week isn’t as high, the training costs aremuch higher, and there’s always the risk of losing oneof those folks just about the time they becomeproductive. Yeah, I’ve heard the mantra “treat themwell and they won’t leave,” but each person isdifferent, and reasons for leaving aren’t alwayslogical. A parent gets sick, a spouse gets transferred, a23-year-old just doesn’t have enough experience to

Page 3: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 3FoxTalk September 1997

reports was placed directly into the hands of the users,thus eliminating my intervention in the report generationprocess. However, I still wanted to know what peoplewere up to when it came to the reports. This required meto take a different approach.

Applications have a tendency to grow in bothcomplexity and functionality over their lifetimes.Eventually your applications might become bloated withan excess of underused functionality. Your applicationsmight have too many reports or functionality that simplyisn’t used. Over time, this bloated condition will cost youa great deal of time and money, because bloatedfunctionality needs to be constantly upgraded as theapplication changes. So the question arises: “How do youfind these bloated areas of your application and eradicatethem?” You can do two things to accomplish this:

1. Sit with all the users of your software and see howthey use it; or

2. Instrument your application to track how it’s beingused.

The first option isn’t very realistic, so you need totake a closer look at the second. Instrumenting yourapplication is actually a simple operation. You simplyneed to figure out which areas of your applications evokeactions and put code in those areas to track their use. TheWindows 95 interface guidelines provide seven basictenets of GUI design. The first says that your applicationshould follow the object-action paradigm. What thismeans is that certain controls are used to invokeapplication functions. In VFP development, two come tomind: menus and command buttons. These two controltypes are the primary controls used by developers toactivate application functionality. With this in mind, youcan begin instrumenting your applications.

Before you can begin, you need to take a look at thetypes of information you should be tracking. You shouldgather the following:

1. What operation was run?2. Who ran it?3. When did the user run it (date and time)?4. How long did the operation take? (This one is optional.)

Instrumenting menusThe first task on the list is to place code in your menus totrack their usage. To track menu usage, you need to doone of two things: 1) place your instrumentation codedirectly into each menu pad; or 2) place your code intoyour central application object that launches different

application components. In either case, you still need toadd code that tracks usage from menus. The followingcode does this:

*-- This function instruments menus.Function g_insmnuLparameters pcPrompt, pcPad

*-- Open instrumentation fileIf Not Used("Instrument") Use Instrument In 0Endif

*-- Get the text from the menu padLocal lcPadPromptlcPadPrompt = prmpad("_msysmenu",pcPad)

Insert Into Instrument (calling_source, ; program_name, other_name, call_date, call_time) ; Values ; ("MENU", pcPrompt, lcPadPrompt, date(), time())

Return

To call this function, place the following function call intoyour menu bars or your global launcher application:

g_insmnu(prompt(), pad())

The primary job of this code is to write your usageinformation to a file whenever someone hits a menu bar.This code uses functions built into VFP that allow you tocollect information about menu hits.

Instrumenting command buttonsYour second task in your instrumentation project is totrack the use of command buttons. Instrumentingcommand buttons should be a rather straightforwardtask; you simply place code into your command buttonbase class that logs your instrumentation information. Thefollowing code can be used to instrument commandbuttons:

*-- This function instruments a command buttonFunction g_inscmdLparameters poCommandButton, poForm

*-- Open instrumentation fileIf Not Used("Instrument") Use Instrument In 0Endif

Insert Into Instrument (calling_source, ; program_name, other_name, call_date, call_time) ; Values ; ("CMD", poCommandButton.caption, poform.Caption, ; date(), time())

Return

To call this function, place the following code into theclick event of your buttons:

g_inscmd(This, Thisform)

So far, this article has covered creating code that can becalled from menus and command buttons. You need to beaware that people can launch forms or application

Instrumenting VFP Apps . . .Continued from page 1

Page 4: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

4 http://www.pinpub.comFoxTalk September 1997

components from alternative sources. The two that cometo mind are check boxes and option groups. If yourapplication launches applications from these or othertypes of objects, you might need to alter the previous codefor insertion into these styles of controls.

Instrumenting formsThere is another interesting object you might want totrack. How about forms? Forms are an interesting type ofobject to instrument because they can be called in manydifferent ways. Forms can be called from a menu, acommand button, as part of a form set, or they can beopened multiple times. This will require you to think alittle differently about how forms are instrumented. Thefollowing function can be used to instrument forms:

*-- This function instruments formsFunction g_insfrmLparameters poForm

*-- Open instrumentation fileIf Not Used("Instrument") Use Instrument In 0Endif

Insert Into Instrument (calling_source, ; program_name, other_name, call_date, call_time) ; Values ; ("FRM", poForm.caption, SYS(16,1), ; date(), time())

Return

The most important piece of this function is the SYS(16,1)call. This call shows where the instrumentation code wascalled. You could also modify this function to loopthrough the calling stack and store the results into amemo field. This would allow you to track the differentareas of your application that might call a common form.

To call this function, you can place the following callinto the Activate, Deactivate, and Init methods of yourforms. This will show how your form is being used inconjunction with other forms:

g_inscmd(Thisform)

Analyzing the resultsAfter running the instrumented version of your software,you can begin analyzing the data that was tracked. Allyou need to do is create a few queries and reports thatwill answer the two critical questions: What features ofyour application are people actually using, and how oftenare they using them?

The first item you will want to query is how arepeople activating items from your menu. The followingquery generates a count of menu hits grouped by themenu pad and the prompt. Querying the data using thismethod takes care of the situations where you have menuprompts with the same names. The query lists the mostactive prompts at the top.

Select program_name as prompt, ; other_name as pad , ; sum(1) as menucount ; From Instrument ; Where calling_source = "MENU" ; Group By 2,1 ; Order By 3 Descending ; Into Cursor c_menucalls

The second query generates results from the commandbutton hits. It groups by form name, then caption fromthe button. It also lists the most active buttons on top.

Select program_name as button, ; other_name as form , ; sum(1) as menucount ; From Instrument ; Where calling_source = "CMD" ; Group By 2,1 ; Order By 3 Descending ; Into Cursor c_buttoncalls

As you can see, with a few simple queries you can gathersome very interesting information about your applicationand its usage. What I’ve found in my instrumentationanalysis is that the 80/20 rule is very true—people use20% of your applications 80% of the time. Does this meanthat you can go in and start ripping out code once you’veperformed an analysis? No! You need to make sure yourun your analysis for a realistic timeframe. This ensuresthat functions that aren’t called frequently (such asmonth-end closing procedures) will have ample time to beinstrumented.

Once you’ve found a function that you want to turnoff, you can simply replace the function calls withmessages like: “Contact MIS to reactivate this function.”This gives users a chance to put the function back at alater date is they need it. After a predetermined time, youcan remove the function permanently if no one hasrequested it.

SummaryWith a few simple functions and queries, you can gathersome rather useful information about your applications.In order to prevent application feature bloat, you need toanalyze your applications and how they’re used withsome objective statistics. Instrumenting your applicationsgives you the information you need to do so. ▲

09PADDOC.ZIP at www.pinpub.com/foxtalk

Rod Paddock is President of Dash Point Software, Inc. Dash Point is a

Seattle-based software development company specializing in Visual

FoxPro, Visual Basic, and Access development. Dash Point has developed

software for clients such as the US Coast Guard, SBT Accounting, Intel,

Pinnacle Publishing, and others. Rod was the lead author for the book

Visual FoxPro 5 Enterprise Development from Prima Publishing. His latest

book is Hands-ON Visual Basic Internet Development from Prima

Publishing. 253-858-5004, CIS: [email protected]

Page 5: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 5FoxTalk September 1997

FoxTalk

Incorporating MS Info intoVFP ApplicationsRichard A. Schummer

Microsoft has included a very powerful applet called Micro-soft System Information with each of the Microsoft Office andVisual Tools. This applet, also known as MS Info, is used to getdetailed information about a PC. MS Info is typically accessedby pressing the System Info CommandButton in the Aboutwindow in Visual FoxPro, Word, Excel, PowerPoint, or Access. Ifit’s available on the user’s system, you can provide it in yourapplication. Rick shows you how to determine whetherMSInfo is available and how to plug it into your system.

CUSTOM applications that have the same look andfeel as other applications have proven to be easierfor training users and reduce support requests.

Adding the System Info CommandButton to a customapplication About window not only provides the samelook and feel, but also gives the support staff thecapability of “checking under the hood” when somethingunexplainable is happening with the application. Thisapplet allows support personnel to determine theconfiguration of an end user’s computer. Informationavailable is as basic as CPU platform, amount of memory,fonts, graphic display features, and printer driversloaded. It also provides in-depth detail about the DLLsthat are loaded and their versions. The information isspecific to the computer on which it is run. One can easilycheck to see whether the system meets minimumconfiguration requirements and check out what else isloaded to see if conflicts exist or an old version of a DLLor printer driver is loaded.

The first cutThe first approach taken seemed simple at first: Drop aCommandButton on a form and modify the Click()method to execute the MSInfo32 executable.

RUN /n1 "C:\Program Files\Common Files\Microsoft _ Shared\MSINFO\msinfo32.exe"

This worked fine on the development machine andseveral other machines that followed the standard installfor MS Office. Then there was the exception—users whodidn’t have SystemInfo installed. And the other

exception—users who had to be different and installedSysInfo in a directory of their own naming. This wasn’t abig deal because there was a check to see whether theexecutable existed before it was run, but users were a bitconfused because their teammates in the next cubiclecould see all this information. This creates a classic case ofinformation envy and provides great material for ScottAdams’ next Dilbert book.

The final solutionSo what’s a developer to do to make this generic? It just sohappens that an entry in the Windows registry reveals theexact location of the MS Info applet. Here’s the entry:

HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Shared Tools\MSInfo

A better solution was to build a class that contains aCommandButton and a registry reader class. A registryreader class performs a number of functions, such asopening the Windows registry, searching for a key,opening the key, and reading the value. There’s a programcalled REGISTRY.PRG that’s included with the VisualFoxPro Solutions that accomplishes the needed action.This example code works great and is fairly easy tounderstand. Another set of registry reader classes are theCSystemSettings/CRegistry classes that are included inVisual Codebook 3.0 by Yair Alan Griver. Both approachesallow easy access to the Windows registry and eliminatesome of the complications of getting the needed values.

The registry entry sub-key for the location of MS Infois “Path.” This can be a little confusing at first becauseyou might just expect the directory path, but theexecutable name is also part of the value.

The first thing to do is read the registry. Once thelocation is determined, place the location path of theexecutable into a custom property for the class. If theentry isn’t found in the registry or the executable isn’twhere it’s supposed to be, the System InfoCommandButton gets disabled. This code is from thectrMSInfo.Init() method, which is included in this month’sSubscriber Downloads.

Page 6: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

6 http://www.pinpub.comFoxTalk September 1997

#DEFINE ccREG_KEY _ "SOFTWARE\Microsoft\Shared Tools\MSInfo"#DEFINE HKEY_LOCAL_MACHINE -2147483646 && BITSET(0,31)+2

LOCAL lcMSInfo && Path to the executable

lcMSInfo = ""

* Get the registry retry for the path to the* Microsoft Shared tool known as MSInfo.lcMSInfo = THIS.ctrRegistry.Get(ccREG_KEY, ; "Path", ; .NULL., ; HKEY_LOCAL_MACHINE)

IF !ISNULL(lcMSInfo) THIS.cMSInfoExeFile = lcMSInfoENDIF

* Disable the commandbutton if registry entry* isn't found or executable file isn't available.WITH THIS IF EMPTY(.cMSInfoExeFile) OR ; !FILE(.cMSInfoExeFile) .cmdMSInfo.Enabled = .F. ENDIFENDWITH

Here’s the code in the cmdMSInfo.Click() method:

* Define a local variable used in macro expansion* because the property can't be macro expanded.LOCAL lcExecute

* Create command to run MSInfo in Normal/Active Mode.* Note the additional delimiters handle the case of* spaces in the string, and that the actual delimiter* used to surround the executable name must be double* quotes.

lcExecute ; = 'run /n1 "' + (THIS.PARENT.cMSInfoExeFile) + '"'

&lcExecute

Legal issuesIt’s important to note that VFP developers cannotdistribute the MS Info executable as part of their customapplications. This is clearly stated in the documentationincluded with VFP. There’s a complete list of files thatcannot be distributed in the LICENSE.TXT file in the mainVFP directory. However, there’s no harm in including thisfunctionality in your apps if the file is already availableon the user’s PC.

ConclusionThere’s nothing better than releasing bug-freeapplications that completely meet a customer’srequirements and expectations. These cases are very rare.The next best thing is lowering training and supportissues and making the life of the customer and thetechnical support staff easy when anomalies are beingtracked down. Tools such as MS Info go a long way inhelping, and adding features like this can give yourapplications a consistent look and feel, which creates amore polished impression. ▲

09SCHUMM.ZIP at www.pinpub.com/foxtalk

Rick Schummer has been developing computer-based solutions for a

Fortune 1 company for the last seven years using FoxPro. After hours, he

enjoys writing developer tools that improve his team’s productivity and

generally hopes to make his life easier. Rick is a founding member and

secretary of the Detroit Area Fox User Group (DAFUG) and president of

the Sterling Heights Computer Club. He is also a regular presenter for

these organizations and other user groups, and is available for

conferences. [email protected]

realize that the grass is pretty darn green around here.A small shop or department only has to lose a coupleof newbies this way to get hurt pretty badly.

• Get more done with the current people—make themwork 80 hours a week. Yeah, right. Any shop thatrequires Herculean efforts from its employees onmore than an occasional basis is being run by a bunchof morons.

• Get more done with the current people—improvetheir productivity. Hmmm, how might that be done?

Here’s what I’ve been thinking about. Let’s suppose thatwe could look at software like it was widgets. In the oldendays (circa 1880), virtually all factories were peopled bycraftsmen. Al and Bob were both making widgets, butthey each did things their own way. They were artisans,craftsmen, individuals. And while you’d prefer a painting

or a symphony to be constructed this way, the finalproduct embodying the artist’s soul and passion, youdon’t want your toaster, automobile, or payroll systembuilt subject to the frailties and idiosyncrasies of anindividual. You don’t want your inventory analysissystem to exhibit flair, personality, and heart.

Frederick Taylor came along around then, andtransformed the construction of widgets from an art to ascience. He was the father of the time-and-motion study—the genesis of the folks in white coats with stopwatchesand horn-rimmed eyeglasses who determine how long itshould take to punch a hole in a widget and move thatwidget with a hole to the next station.

He broke down each job into the smallest possiblecomponents, analyzed each piece, and then assembled thejob again, eliminating waste and reducing the chance forerror and risk. The result was more work per unit of time.And those who bought into it made more money, got hurtless often, and produced better products. The downside?I’ll get to that in a minute.

It’s 1997. Why aren’t we doing this with software?Some firms are trying. They create analysts, who meet

Editorial . . .Continued from page 2

Page 7: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 7FoxTalk September 1997

with customers, determine what needs to be done, andwrite up specifications. The specs are then turned over tothe mole people—programmers who are locked in tinylittle rooms, fed a steady diet of Mountain Dew andDoritos, and given T-shirts when a project is finished.

There are two problems with this scenario. The first isthe silly argument that making manufacturing efficientdehumanizes people by trying to turn them intomachines. A job was no longer an art—it was rote work.Put Framboozle A into Thingamabob B. Over and over.There was only one right way to do something. It robbedpeople of their individuality, of their contribution to thework. It turned human beings into mechanical drones.

I would agree, except for one point. If done properly,this system actually empowers people. What shouldhappen is that those folks with the ability to contribute tothe art get involved with the aspects of the process thatrequire creativity. And those who are given the gruntwork? What can happen is the opening of opportunities topeople who before couldn’t have gotten involved at all.

Take the job that involved some boring and repetitiveactions. There are plenty of people who need consistencyand order in their daily routine. People who don’t reactwell to change, who can’t adapt to a multiplicity ofconditions—for these folks, a “Taylor-ized” job can be astep up in their life. The key is to match the people withthe task correctly.

Okay, so you’ve got images of Hell’s Angels and ManMountain Mikes pushing around large blocks of iron withtheir bare hands, and Wally Cox running around in awhite shirt and penny loafers trying to examine what theydo and not get killed in the process. Let’s jump into thetypical software development shop. Can we really affordto have some dweeb with a white coat and stopwatchmeasure the time it takes us to put a multi-column list boxon a form?

Let’s paint an ideal picture and then figure out how toget there from reality.

In the ideal shop, we’d have an analyst (that wouldbe the department head or the owner), a couple ofprogrammers of varying skill levels, and a QA person.Perhaps an administrative assistant as well. Suppose thecompany (or department) is smaller. Not as manyprogrammers, and the QA person and admin assistantlive in the same body. If the company/department isbigger, perhaps another analyst, a couple moreprogrammers, and if it’s any larger than that, then workgroups get set up.

When a job comes in the door, the analyst goes off tothe customer’s site to write specs. Let’s talk about specsfor a minute. (Actually, I could talk about specs for severalhundred pages, but my boss is already making thoseslashing motions across his throat.) Specifications are likeblueprints. It’s just that software isn’t like metal. If youmake a mistake with a ton of iron at 2,000 degrees, you’ve

cost the company a lot of money. If you make a mistakewith a class library that’s going to be used by two dozenapplications, well, hey, it’s just software, right? It’s notreal, after all. All together now: nyuk, nyuk, nyuk.

So maybe blueprints are a good idea. And, likeblueprints for a house, there are two kinds. The first kindis the set of renderings that the buyer sees—what thehouse will look like, inside and out. But it doesn’t listevery 2x4, electrical socket call-out, or plumbing joint.These renderings are equivalent to the functional spec—“what you can do with your software.”

The second kind of blueprints is the internal set,which describes the thing being built down to the last boltand quarter-inch measurement. We refer to these as thetechnical specs—they describe file layouts, internalprocesses and rules, ERDs, business objects, and so on.

Now that we have all this done, we can turn it over tothe programmers, right? Well, maybe not ’til next week.We’ve got more work to do.

Fundamentals. Training. Getting good at the basics.How many of you have been through C&F with yourprogrammers, page by page, making sure theyunderstand the 200 tools you use most often? No? Anyspecial reason? Maybe you’re just assuming they know itall? (Not a good idea, trust me.)

How many of you have company coding standards?Using [] instead of () with array names. Namingconventions. When to use integer data types and when touse numeric. How to handle commenting. Agreement onwhen a view is more appropriate than tables.

Remember Taylor—that fellow I mentioned a fewparagraphs ago? This training is required if you’re goingto break down software development into the smallestcomponent pieces. Your programmers aren’t going to beable to assemble a parent-child screen that processesmultiple types of Medicare payments unless theyunderstand that your company standards only use arraysand SQL SELECTS to populate combo boxes, and alsounderstand when to use each.

OK, let’s assume that this has been done. Anythingelse? Well, we have tight technical specs, and we havewell-trained developers. There still seems to be a linkmissing. If you wander through a factory, you’ll seedetailed descriptions of how the work is to bemanufactured. Step 1: Put Framboozle A intoThingamabob B. Step 2: Turn Thingamabob B on its back.Step 3: Put two screws into the open holes. Step 4: You getthe idea.

All we have to do is create the same type ofspecifications for writing software. Break each page of thefunctional and technical specifications into step-by-stepinstructions and hand them over to the appropriate levelof programmer.

Wow, that’s hard work. Maybe we should just starteating our children instead. ▲

Page 8: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

8 http://www.pinpub.comFoxTalk September 1997

Keep YourEffective ConventionsSteven Black

Last month, you saw a startling proposal—one that suggestedthat the naming conventions that many of us have used beabandoned because they promoted poor programmingpractices. This month, you’ll see the first of several rebuttalsthat offer a variety of opinions on this same subject.

CHARLIE Schreiner presents compelling argumentsfor more natural-language code in sources. (See “AcRose by Any Other oName,” in the August 1997

issue.) In my opinion, it’s quite all right to question whywe do things. I agree with some of Charlie's conclusions,and I disagree with some others. If life were simple I’dagree with the works, but as we all know, life ain’t simple.But I sympathize nonetheless.

Charlie has some valuable things to say, but hisconclusions are sometimes objectionable because theunderlying assumptions are somewhat shaky. Forexample, I wish that Charlie had better balanced some ofhis arguments. Comparing variables lnCt andNumberOfRows for readability is a self-servingconcoction. If, instead, we compare lnRowCount andRowCount for readability, the issue is much clearer. Is itsensible to name code elements clearly? Absolutely! (Herewe agree.) Is the difference between lnRowCount andRowCount such a big deal? In my opinion, no—they’reabout equally readable (in other words, here we disagree).To me, the prefix conveys vital information that aids myunderstanding.

Likewise, in the section on parameter naming, wherethe convention is to use a “t” prefix, why does Charlieargue: “paraDoesn’t paraIt paraSlow paraDownparaCommunication paraTo paraPrefix paraWordsparaLike paraThis?” Uh, to be fair, shouldn’t that be“tDoesn’t tIt tSlow tDown tCommunication tTo tPrefixtWords tLike tThis”? See how this makes all thedifference? The prefix “t” is almost innocuous, whereas“para” is intrusive.

So let’s at least balance the argument.Elsewhere Charlie says, “I have yet to see a valid

reason to make anything public.” Nonsense! I

FoxTalk

occasionally use globals; so does Ken Levy and so domany VFP frameworks. Charlie’s argument—thecornerstone for the case against the Hungarian “g”prefix—is highly suspect. I have no problem whatsoeverwith “all variables are LOCAL unless otherwise noted,”but it doesn’t follow that all prefix scoping should beeschewed.

Nonetheless, I feel that Charlie has an importantmessage. VFP is a radical departure from FoxPro. We arewriting object-oriented code, and few of us writemonolithic procedures anymore. I think that Charlie isquite right in questioning conventions that wereoriginally suggested to support FoxPro and are nowblindly applied to Visual FoxPro.

The formality of conventions is a matter of degree,and, like things on a continuum, a happy medium israrely found at an extreme end of the scale. Moreover,what’s right for one developer isn’t necessarily right foranother. I don’t expect what works for me to work foreveryone, and it’s quite natural that we find diversity ofopinion. I personally abhor gonzo convention rules, justas I abhor a convention vacuum. Somewhere in betweenis a point that’s right for me and my team.

If you code alone, and if you maintain all your owncode, then you can pretty much do as you like. Noproblem!

If, on the other hand, you work in a team, or someoneelse will be taking over your code, or others will bereviewing your code, then nothing is worse than the lackof a reasonable coding standard. There’s a high potentialfor ambiguity to not have clear data names (Charlie’sexcellent point) and prefixes for data-type and scope.

If your code is ever submitted to a peer reviewprocess, then it will be examined line-by-line by peoplewho don’t live in the code but who want to examine it, asquickly as possible, for correctness. The same is true forother developers who just want to peruse prior to reuse.These people need assurances for functionality andquality. Without coding conventions, this is very difficultto do.

Page 9: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 9FoxTalk September 1997

Actually, without coding conventions, the code lookslike it was written by an amateur. This might not betrue—after all, one shouldn’t judge a book by its cover—but the first impression remains. Why do you dress neatlyto visit clients? Because that’s what’s expected of aprofessional. It also puts you, at least in appearance, inthe correct mindset. The same is true for your code. Freecode is sloppy code, at least in principle.

Back in 1994, I formally documented Ken Levy’sGENSCRNX because it was part of the INTL Toolkit. Kendidn’t use code conventions in those days, and I’ll tellyou, those were weeks of hell for me. We don’t writemonolithic .PRGs like that anymore (thank God), but ifKen had used simple conventions, my job would havebeen much easier. By the way, since his days at Flash, Kenis a code convention convert, and I think the quality of hisrecent work is all the better for it.

Here’s a summary of the situation as I see it:

• Scoping prefix: I agree almost completely withCharlie here. In object-oriented code, if we agree thatvariables are LOCAL unless otherwise noted, thenwhy bother with a scope prefix for locals? For globalvariables, whether we use gGlobal or _Global (asCharlie suggests) is immaterial, as long as whatever isused among collaborating developers is consistent. Inany event, globals are rare, so who really cares aboutthe marginal difference in readability between gThisand _That?

• Data type prefix: I strongly disagree with Charliehere: I certainly won’t abandon type prefixes. VFP is aweakly typed language; we don’t have a compiler tonag us about invalid data types. The suggestion thatdata typing should be eschewed in data naming forthe sake of marginal readability is borderlineirresponsible. Data type prefixes are essential to guidefuture maintainers through the process ofunderstanding your code.

• Parameter prefix: I’ll continue to use the parameterprefix “t” because it’s often handy to distinguish theparameters in the method code. Also, in some of ourmethods, the parameter is often of unknown type—itcould be anything. We can’t overload methods in VFPlike we can in C++, so the tx or tu (x or u forunknown) is an extremely handy marker to note tohandle different data types appropriately. Besides, asI’ve already shown, the “t” is a relatively innocuouscharacter that, in my opinion, has little adverse effecton readability.

• Array prefix: There are many array functions in VFP(ASCAN(), AMEMBERS(), ALEN(), and so on) where

the array is passed by name. This is contrary toCharlie’s argument that the array variables alwayshave [square] brackets. They clearly don’t, nor dostatements like aAddress=''. I intend to keep the “a”prefix in array names, especially for array members.Besides, some folks (like me) sometimes forget anduse regular parentheses in arrays, and occasionallybecause the [square] brackets are also characterdelimiters of last resort.

• Table and field names: Given theCURSORGETPROP() function, there’s little need tobrand primary key fields with pk, which is somethingfew of us ever did anyway. I also agree that rules forunique field names, or weaving a table name cookieinto the field name is of little use unless you haveunderlying mechanistic maintenance ordocumentation processes that rely on such things. SoCharlie, here we mostly agree.

• Class members: I have mixed feelings on the issue ofnaming custom properties without regard to type. Iprefer to use a type prefix because this helps meavoid mistakes. I note that when dealing with built-inproperties like BorderStyle, the properties sheet helpsme to match the type number with a description ofthe setting; moreover, VFP won’t let meprogrammatically assign an invalid type here. Mycustom properties have none of these features,unfortunately, so the type prefix is very valuable.

In closing, not all our traditional conventions shouldbe rigorously applied. But in the end, this has more to dowith reliability than readability. Given the choice, I prefercode that’s more reliable and maintainable, because bydefinition, once that’s given, one spends less time there.For me, what feels right in VFP is data naming withexplicit data type and, where appropriate, explicit scopeprefixes. These characters can co-exist with a high degreeof clarity and readability. Charlie has failed to convinceme otherwise.

To quote Steve McConnell, author of Code Complete:“The power of naming conventions doesn’t come from thespecific convention chosen but from the fact that aconvention exists, adding structure to the code and givingyou fewer things to worry about.” ▲

Steven Black is a developer in Kingston, Ontario, who specializes in

multilingual and international applications. Steve also does OOP training

and offers specialized advanced VFP consulting to other developers. He is

the author of Steven Black’s INTL Toolkit, a framework for multilingual

development in FoxPro 2.x, 3.0, and 5.0. He has been a featured speaker

at many FoxPro conferences and occasionally darkens the pages of

FoxTalk and FoxPro Advisor newsletters. [email protected],

www.stevenblack.com.

Page 10: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

10 http://www.pinpub.comFoxTalk September 1997

Calling All WindowsGary DeWitt

Extending VFP Through the API FoxTalk

Have you ever seen an application that does something reallycool but that Visual FoxPro won’t let you do? Whatever it is, itcan be done with the Windows Applications ProgrammingInterface (API), most of which you can call from your VFPapplications. This article is the start of a new column, ExtendingVFP through the API, dedicated to showing you how toincrease the robustness of your VFP applications by unlockingthe power of the Windows API.

MICROSOFT wrote the Windows API to expose theoperating system’s functionality to applicationsdevelopers. Anything that you do in a Windows

application ultimately calls the Windows API. Fortunately,Visual FoxPro handles most of these API calls for us. Iwrote my first Windows program in C, hand-coding everyAPI call myself. While it was a memorable experience, it’sone I don’t wish to repeat! FoxPro does a terrific job ofhiding the complexity of the Windows API so that you canquickly write good-looking Windows applications.

But FoxPro doesn’t do everything. The designers ofVisual FoxPro knew that and gave us a tool we can use tocall the Windows API ourselves, giving us the means toextend the functionality of our applications by takingdirect control of the operating system. This tool is theDECLARE – DLL statement. By using DECLARE, you cantell VFP how to find and call API functions. Here’s anexample of the most basic Windows API call:

DECLARE INTEGER GetActiveWindow IN Win32API

Here’s how to translate this statement. The function iscalled GetActiveWindow, and the statement is called adeclaration. This declaration of GetActiveWindow tellsVFP that it’s a function that takes no arguments, returns aninteger, and can be found in Win32API. The Windowsoperating system is primarily made up of a bunch of DLLs.Five of these—USER32.DLL, KERNEL32.DLL, GDI32.DLL,MPR.DLL, and ADVAPI32.DLL—are collectively known asWin32API. Functions found in other DLLs, such as printerfunctions in WINSPOOL.DLL, require that you specify thename of the DLL in your declaration.

Once a function has been declared, you can call it justas you would any built-in or user-defined function inFoxPro.

* <some FoxPro code here>nHandle = GetActiveWindow()* <more FoxPro code that does something with nHandle>

Windows API step by stepThere are a couple of things to keep in mind about usingthe Windows API. First, Windows assigns numbers, calledhandles, to all resources. If you want to refer to a resourcein an API function, you must usually first call anotherfunction to get a handle for that resource. Second, theWindows API was written by and for C programmers.Most C programs don’t pass values as arguments; theypass pointers. Pointers are nothing more than addresses ofvalues. In FoxPro, passing by reference actually passes ahidden pointer to the API function, so you will usuallypass by reference.

There are normally five steps to take when using theWindows API:

• Declare the API functions.• Get a Windows handle to the resource you wish to

control.• Initialize memory variables for passing to the API.• Call the function, passing arguments by reference.• Use the values returned by the API function.

Here’s a short program that uses the Windows API tofind the text of the title bar of the main VFP window. Thesame thing could be done simply by checking the value ofthe _SCREEN.Caption property in VFP, but this codedemonstrates each of the steps you must usually takewhen using the Windows API.

*-- Declare two API functionsDECLARE INTEGER GetActiveWindow IN Win32APIDECLARE INTEGER GetWindowText IN Win32API ; INTEGER, STRING @, INTEGER

*-- Get the handle to the main VFP windownHandle = GetActiveWindow()

*-- Initialize variableslcText = SPACE(100)lnSize = LEN(lcText)

*-- Call the API functionlnSize = GetWindowText(nHandle, @lcText, lnSize)

*-- Use the resulting valueIF lnSize > 0 ?LEFT(lcText, lnSize)ENDIF Continues on page 24

Page 11: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 11FoxTalk September 1997

Use Version Controlfor Added SafetyJefferey A. Donnici

Best Practices FoxTalk

Whether working as part of a large development team orcompletely alone, a solid version control system provides alayer of safety, and several other benefits, to the developer.Now that version control systems can be integrated directlyinto the Visual FoxPro 5.0 development environment, reapingthese rewards is even easier. This article takes a look at avariety of issues surrounding version control software andexplains why all developers can gain by implementing aversion control system.

IMAGINE the following scenario: You come into theoffice one day with a brilliant idea for how to simplifya particularly confusing, and time-consuming, piece of

code. You spend the better part of a day working on it.You change it, compile it, and run it, and then you go backand change it again. This cycle repeats itself over and overagain until the end of the day. Suddenly you realize thatyou probably should have left it alone and that, in fact, itworked much better yesterday when you went home forthe day. Yes, you decide, we should definitely go withwhat we had yesterday.

Do you have a backup?Suppose you did make a backup last night of all your

development work, and getting back to yesterday’sversion is no problem. What if the version that works bestand you feel most comfortable with is the one from earlierin the same day, perhaps just prior to lunch? Did youmake a backup then? If you’re like me, you’re notstopping to make a complete backup of your work everysingle time you save a class or program. That can make itvery difficult to get back to a specific point in time.

This is one of the primary reasons that nearly alldevelopers, regardless of what language they’re using orthe type of work they’re doing, can reap benefits from theuse of a good version control system. While the typicalsituation will be a developer using Visual SourceSafe 5.0with Visual FoxPro 5.0, many of the principles and ideasdiscussed here will be applicable to development work inother languages, or with other version control systems.

A version control primerPut simply, a version control system acts as a library foryour source code, project files, and just about any othertype of file for which you might want to keep a revisionhistory. The idea is that a developer must “check out” afile before making any modifications to it. In the processof checking the file out, a copy of the most recent fileversion is placed on the developer’s local machine. Withthat done, the developer can work on the file to his or herheart’s content. There are no restrictions or limitationsfrom the version control system placed on what can orcan’t be done with that local copy. When the developerhas made all of the changes, he or she can then check thefile in to the version control system. Upon doing so, thedeveloper typically will provide a description of thechanges made, why those changes were made, and anyother comments pertinent to that “version” of the file.

At that point, the newly checked-in version of the fileis available for other developers to use. If they want tomake use of the newer version of that file, they can simplyget the latest version out of version control. If they wantto make their own modifications, the file is available to bechecked out yet again.

Note that only the changes to the original file arechecked in when the developer has finished with thesource file. By using this method, the size of the versioncontrol database stays at a manageable level rather thanincreasing by the complete file size with each iteration.

Version control software also maintains a history foreach file stored in the system. This provides the ability toeasily see what has changed, when it changed, why it waschanged, and by whom. When tracking down a bug in asystem, this information can be critical to finding andfixing the problem. Also, because the database containsthe changes that were made to the original file, it’s veryeasy to get back to any earlier version. This is particularlyhelpful when the development versions on your machinedon’t match the version that was built for a client. Bygoing back to the versions with which that client’s build

Page 12: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

12 http://www.pinpub.comFoxTalk September 1997

was created, you can get an exact duplicate of the versionthey’re using. A “labeling” feature in the version controlsoftware makes this even easier. For example, when a bigrelease milestone is reached, you can quickly create a“labeled version” for every file in the system. That makesit easy to figure out which versions of files were used forspecific application releases.

Most good version control systems will also provide a“branching” feature. This allows you to “branch” yourproject at a specific point in time into two differentdevelopment paths. In doing this, those paths becomeindependent projects on their own that can take on theirown changes and updates. For example, you mightchoose to branch some files in your application when it’sfirst released. This allows you to have one developmentpath for continual additions and enhancements, while theother represents what the user currently has and only seesvery minor changes or bug fixes.

Version control issues with Visual FoxProWith the release of Visual FoxPro 5.0, using a versioncontrol system on a VFP project has become much easier.Microsoft has added the ability for the Project Manager tohook into any version control system that complies withthe Source Code Control API. This means that thedeveloper can simply right-click on any file in the ProjectManager and choose to check it out, check it back in, getthe latest version, and do other common version controltasks. With a source control system integrated into VisualFoxPro 5.0, the Project menu will also have a SourceControl sub-menu that allows you to perform nearly allversion control operations.

Before that can happen, however, it’s important tomake sure that Visual FoxPro has been configured to useyour source control system. This is done in the Tools-Options dialog, on the Projects tab (see Figure 1). Thefollowing is a brief summary of each of the configurationoptions available to you:

• The “Active source control provider” allows you tochoose which of the version control systems installedon your machine will be used by the Project Manager.

• The “Automatically add new projects to sourcecontrol” checkbox indicates whether a new project,created from either the File-New menu or theCREATE PROJECT command, will automatically beadded to your version control database. You will beprompted for the name and location you want thisproject to have in the version control database.

• The “Check out files upon modify” checkbox lets youdecide whether files you open for modification in theProject Manager should be automatically checked outof the version control system.

• The “Add files to source control upon add” optionlets you tell the Project Manager to automatically addany file you add to the project to the version controlsystem.

• “Remove files from source control upon removal fromproject” provides the exact opposite service as theabove checkbox, removing any file from sourcecontrol that you remove from the Project Manager.

• The “Display dialog box for shortcut menucommands” lets you tell the Project Manager todisplay a dialog box when you right-click on a fileand perform a source control task. This dialog boxwill provide you an opportunity to select multiplefiles and perform other “extended” tasks associatedwith the source control function.

• Finally, the “Text generation” textbox lets you specifya program that creates a textual representation of anybinary file you’re checking in.

That last option is especially noteworthy, and itbrings up an important point related to using sourcecontrol systems with VFP. One of FoxPro’s primarystrengths has long been the open architecture it providesfor dealing with the components of an application.Components such as visual classes, forms, reports, andmenus are themselves stored in FoxPro tables. Because wecan write VFP programs to open and manipulate thesefiles directly, we have a great deal of flexibility inextending our development efforts.

Figure 1. The Projects tab within the Tools-Options dialog ofVisual FoxPro 5.0 provides a variety of options for integrating aversion control system into the Project Manager.

Page 13: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 13FoxTalk September 1997

However, two useful features of many source controlsystems are to allow the developer to visually comparedifferent historical versions of a file and to allow themerging of a single file that has been simultaneouslymodified by different developers. These features requirethat the file being compared or merged be a standard textfile. Since that’s not the case with VFP, there must be away for the metadata tables the components are stored into be converted into simple text files. A program thatships with Visual FoxPro 5.0 named SCCTEXT.PRGperforms this task. Upon checking in a file, this programwill be run and a text representation of the componentwill be added to the source control database along withthe component. In doing this, you can then select twoversions in history and compare their textualrepresentations. Unfortunately, this program has atendency to have problems with metadata tables that areespecially large. Once again, though, the fact that it’s asimple FoxPro program file means that you can alwaysopen it up and modify it to meet your needs. Just makesure you have a backup of the file before changing it, or—what the heck—just add the file to your version controlsystem! If you choose to leave the “Text generation”textbox empty and don’t specify any generation program,no text representation will be added to your versioncontrol database.

Even if you’re not using Visual FoxPro 5.0, it’s stillquite easy to implement version control on your project.Just remember that the majority of FoxPro’s componentsare made up of two different files. For example, a visualclass library is simply a table with a .VCX extension andan associated memo file with a .VCT extension. When youcheck a visual class library out of your version controlsystem, be sure to check both of these files out. If youcheck only one of them out and make changes to thecomponent, you’re likely to have all sorts of problemswith the table and the memo file being out of sync. Othercomponents that fall into this category include forms(SCX/SCT), reports (FRX/FRT), menus (MNX/MNT),and even the project itself (PJX/PJT). If you’re placingyour project’s database container into version control,remember that there are actually three files to be dealtwith: the DBC (the table itself), the DCT (the table’s memofile), and the DCX (the table’s index file). Be sure toremember any corresponding index and memo files for allthe tables you add to version control. Also, because yourversion control system won’t be integrated into the ProjectManager, you’ll usually want to have that softwarerunning separately, using Alt-Tab to switch between itand VFP.

If you’re running a version of FoxPro earlier than 5.0,or if you choose not to use the text-generationfunctionality in Visual FoxPro 5.0, you’re not going to beable to provide for multiple check-outs on a file. When afile is simultaneously checked out by two people, the

version control system must be able to provide thedevelopers with a mechanism for merging both of theirchanges to that file. Once again, this functionality won’twork with binary files such as those that make up muchof FoxPro’s metadata. My experience is that there arerarely times when two developers need to be working onthe exact same file at the same time. FoxPro provides somuch flexibility in how you can store your variouscomponents that there isn’t usually a problem with filecontention. Still, you can see how it might be a problemwith a program like Microsoft Access, which stores all ofits components for an application into a single file.

Managing projects with version controlWhen using a version control system, there are a varietyof issues that come up in relation to VFP project files. Besure to keep these issues in mind:

• A FoxPro project can’t be opened if the project file isread-only. Often, when getting the most recentversion of a file from a source control system, the filewill be copied to your local machine as a read-onlyfile. Most systems will let you override this behavior,so you’ll want to make sure that the project is writtento disk as read/write. Otherwise, you’ll need to useyour file system (either a DOS prompt or theWindows Explorer) to remove the read-only attributefrom the project files.

• With a team of developers, each person will need alocal copy of the project to work with, including theproject files themselves. Because only one person willtypically check the project file out at a time, it’simportant that the project file be checked back in on aregular basis. Remember this when you add a newcomponent to a project developed by a team. Notonly will you need to check that component in for theothers to access, but you’ll also need to check in theproject file that has the new component added.Unless you do this, other developers will have a localcopy that’s out of sync with the shared copy in thesource control database.

• Remember that not all files need to be checked in tothe version control system. As an example, .FXP filesare compiled FoxPro programs and shouldn’t beincluded in the source database. As long as theoriginal PRG source code is maintained by theversion control system, the FXP will be generated asnecessary. The same principle applies to .EXE and.APP files, as well as program files that are created byFoxPro from the original metadata tables (the MPRfile for menus and SPR files for screens in 2.x versionsof FoxPro).

Page 14: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

14 http://www.pinpub.comFoxTalk September 1997

If you’re working as part of a team of developers,it’s a good idea to put some standards in place so thatconflicts between different versions and developmentpaths are eliminated. For example, it’s probably a goodidea to have a development lead for each project who isresponsible for making sure that the project file is up todate and checked in often. This will help to make surethat there is a single point of contact for changes to theproject table itself. When other developers need to addor remove a component from the project, the leaddeveloper for that project should handle those changes.You might also want to state that no changed file,regardless of its type, should be checked in to theversion control system until it has been tested. Not onlyshould that file be tested individually, but tests alsoshould be run to make sure that the changes to the filedidn’t cause a problem somewhere else in the project.Obviously, the debugging process for applications isan ongoing effort, and the components of a project willinvariably be found to have bugs. Still, developersshould be able to expect a reasonable level of stabilitywhen something new or changed has been added to theversion control database.

Most version control systems include some level ofsecurity functionality, so you might also want to assigncertain levels of permissions or rights to certain projects.This can help to make sure that only the appropriatepeople are making changes to a specific project and canalso prevent accidents that occur when a developer checksout a file other than the one he or she intended.

Do you need version control?Deciding whether or not you need a version controlsystem shouldn’t take long. If you’ve ever wanted toundo a change you’d just saved, or if you’ve ever wishedthat you could get back to last week’s or last month’sversion, you’re probably a good candidate. Further, if youlike the idea of always having a redundant backup ofyour development work, you probably see the value inimplementing version control.

Look at some of the abilities and features that versioncontrol provides that would be difficult, if not impossible,to get without it:

• The backup you perform on your machine regularly(we all make backups, right?) will be twice as safebecause a second backup of the source code changesexists in the version control system’s database.

• You can quickly get to any version or “point in time”for any file that’s been added to the version controldatabase.

• You can eliminate a situation where multiple peopleworking on the same file at the same time haveconflicting changes to deal with.

• You can quickly get the latest versions of all yourdevelopment files by simply telling the versioncontrol system to refresh your local machine withthose source files.

• You can get a complete history and list of changes forany file.

Also, remember that any file at all can be checked into a version control system. It doesn’t necessarily have tobe a source code file for a development project. If a filechanges over time and you want the ability to see ahistory of those changes, or even go back to a point priorto any change, then add that file to your version controlsystem. On our development team, we often have Worddocuments in our source control system that providedocumentation and implementation instructions for someof our components. When one of those componentschanges, the Word document might also be checked outand updated. Also, it makes it easy for a developer whowishes to use a component to get the documentation for itat the same time. I even use version control on mystandalone machine at home. It’s handy for personalprojects such as article drafts, web pages that I’mupdating, or even the occasional VFP project.

A bit of housekeepingAs you might know, this is my first appearance in the BestPractices column. Doug Hennig has moved on to write anew FoxTalk column dedicated to reusable tools forFoxPro developers. If you’re like me and really appreciatethe benefits and efficiency associated with reusability,you’ll want to check out Doug’s new column. Given allthe insight he’s provided to us with past Best Practicescolumns, I’m certain it will be a valuable feature eachmonth. Despite the tough act that Doug has left for me tofollow, I plan to bring a new Best Practice to you eachmonth in this space. If you’ve got a practice or methodyou’d like to share with the rest of us, or if you just wantto discuss something I’ve mentioned here, feel free todrop me a line. ▲

Jefferey A. Donnici is an applications developer with Resource Data

International, Inc. in Boulder, CO, who specializes in the issues

surrounding a common code library and developer’s tools. Jeff is a

Microsoft Certified Professional in Visual FoxPro and a Microsoft

Developer MVP. 303-444-7788, fax 303-444-1286,

[email protected], [email protected].

Page 15: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 15FoxTalk September 1997

Reusable Tools FoxTalk

It Just Kicks inDoug Hennig

The best kind of tool is one that automatically does its jobwithout having to set any properties, write any code, or evenlift a finger. This column looks at a strategy for implementingthese types of tools and provides a pop-up calendar for Datefields and a universal find function for your applications.

ONE of the things I love about object orientation isinheritance. If you make a few tweaks to a class,any subclass or instance of that class automatically

gets the new behavior or appearance without touchingthem. I refer to this phenomenon as “it just kicks in,”because the new behavior just kicks in without any effort(other than making the change to the class in the firstplace). This was really driven home recently when Iadded a powerful new feature to our base classes (the“find” functionality described in this column), and thisnew feature “just kicked in” on every form a co-workerhad created for a client application the next time sherebuilt the project. She was absolutely stunned at howlittle effort there was on her part (none, actually <g>).

My idea of the ideal tool is one that “just kicks in”like this. We won’t often hit this ideal, because some toolsare just too complex or unwieldy to add to our baseclasses. However, in this month’s column, we’ll look at astrategy for implementing “just kicks in” tools and thenlook at a couple of extremely useful tools of this genre: apop-up calendar for Date or DateTime fields and auniversal “find” feature for your applications.

The strategyBefore we start, I’d like to publicly acknowledge the debtof gratitude I owe to two FoxPro gurus: Steven Black andKen Levy. The ideas expressed in this column were eithershamelessly stolen from them or were inspired by theirideas. Thanks especially to Steven for introducing andmaking more understandable some of the design patternsused in these tools.

The strategy I took to create new functionality in ourTextBox base class (SFTextBox, described in my February1997 column and included in the Subscriber Downloadsin SFCTRLS.VCX) is based on a shortcut menu. Shortcut

menus, which are typically displayed when the user right-clicks on an object, are really easy to do in Visual FoxPro5.0, thanks to the new SHORTCUT clause in the DEFINEPOPUP command. Here’s an outline of how this strategyworks:

• The RightClick() method of SFTextBox calls a customShowMenu() method.

• ShowMenu() defines a shortcut menu calledSHORTCUT (rather than using the Menu Designer tocreate a shortcut menu file, I added the code directlyto SFTextBox so it’s as self-contained as possible) andthen calls the custom method ShortcutMenu() topopulate the menu. Why not put the code to populatethe menu directly into ShowMenu()? So you cansubclass SFTextBox or even change the behavior of asingle instance to create specialized versions thatoverride the contents of the shortcut menu. Theproblem with putting all the code into ShowMenu() isthat you can’t add new items to the shortcut menuwithout copying and pasting code from the baseclass, since the menu population code must bepreceded by menu definition code and followed bymenu execution code. Putting this code into separatemethods makes creating specialized versions easy.

• SFTextBox has a custom property called oHook,which provides the capability for hooking objectstogether. While a complete discussion of hookingobjects is beyond the scope of this article (see StevenBlack’s article, “Hook Operations,” in the August1997 issue), here’s a brief description. oHooknormally contains .NULL; however, you can store areference to another object in oHook (this might bedone in the Init() method of an instance or subclass ofSFTextBox, for example, or perhaps by another objectsuch as a form the instance sits on). After callingShortcutMenu(), ShowMenu() checks to see if oHookpoints to a valid object and if that object has aShortcutMenu() method. If so, that object’s method is

Page 16: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

16 http://www.pinpub.comFoxTalk September 1997

called. This provides the ability to hook an instance orsubclass of SFTextBox to another object that addsadditional items to the shortcut menu. We won’t useoHook in this article, but we might use it in futurearticles to add additional features to objects withouthaving to subclass them or modify their basebehavior.

• After the menu has been populated withShortcutMenu() (and possibly a hooked object’sShortcutMenu()), ShowMenu() activates the menu.

Here’s the code for ShowMenu():

local loObject

* Define a reference to this object in case the action* for a bar is to call a method of this object, which* can't be done using "This.Method". ("This" isn't* applicable in a menu.) The action can be specified* as "loObject.Method".

loObject = This

* Define the menu.

release popup SHORTCUTdefine popup SHORTCUT shortcut from mrow(), mcol()with This

* Populate it using a custom method.

.ShortcutMenu()

* Use the hook object (if there is one) to do any* further population of the menu.

if type('.oHook') = 'O' and not isnull(.oHook) and ; pemstatus(.oHook, 'ShortcutMenu', 5) .oHook.ShortcutMenu() endif type('.oHook') = 'O' ...endwith

* Activate the menu if necessary.

if cntbar('SHORTCUT') > 0 activate popup SHORTCUTendif cntbar('SHORTCUT') > 0release popup SHORTCUT

The ShortcutMenu() method can add anything youwish to the SHORTCUT pop-up. Here’s what I have inmine:

• Cut, Copy, Paste, Clear, and Select All: Thesefunctions are easily implemented using the newSYS(1500) function (assuming you have an Edit padin your menu bar).

• Calendar: If the textbox holds a Date or DateTimevalue, a Calendar bar appears. Choosing this bar callsthe custom ShowCalendar() method of SFTextBox.Note that ShowCalendar() and other methods ofSFTextBox have to be called using a variable(loObject, which is defined in ShowMenu()) ratherthan “This” as the object name, because “This” isn’tvalid in menu code, which executes outside the scopeof the object.

• Find: SFTextBox has a custom property called lFindthat’s set to .T. by default. If lFind is .T. and theobject’s ControlSource is filled in, ShortcutMenu()adds a Find bar to the menu. This bar calls the customFindField() method when it’s chosen.

Here’s the code for ShortcutMenu():

local lcRow, ; lcCol, ; lnBarswith This

* Calculate current row and column coordinates as* strings so we can macro expand them.

lcRow = ltrim(str(Thisform.Top + .Top + mrow())) lcCol = ltrim(str(Thisform.Left + .Left + mcol()))

* Add cut, copy, paste, clear, and select all items to* the shortcut menu.

define bar 1 of SHORTCUT prompt 'Cu\<t' define bar 2 of SHORTCUT prompt '\<Copy' define bar 3 of SHORTCUT prompt '\<Paste' define bar 4 of SHORTCUT prompt 'Cle\<ar' define bar 5 of SHORTCUT prompt '\-' define bar 6 of SHORTCUT prompt 'Se\<lect All' on selection bar 1 of SHORTCUT ; sys(1500, '_MED_CUT', '_MEDIT') on selection bar 2 of SHORTCUT ; sys(1500, '_MED_COPY', '_MEDIT') on selection bar 3 of SHORTCUT ; sys(1500, '_MED_PASTE', '_MEDIT') on selection bar 4 of SHORTCUT ; sys(1500, '_MED_CLEAR', '_MEDIT') on selection bar 6 of SHORTCUT ; sys(1500, '_MED_SLCTA', '_MEDIT')

* If this field contains a Date or DateTime value,* add a Calendar bar.

lnBars = 6 if type('.Value') $ 'DT' define bar 7 of SHORTCUT prompt '\-' define bar 8 of SHORTCUT prompt 'Cal\<endar' on selection bar 8 of SHORTCUT ; loObject.ShowCalendar(&lcRow., &lcCol.) lnBars = 8 endif type('.Value') $ 'DT'

* If we can find on this field and we have a* ControlSource, add a Find bar.

if .lFind and not empty(.ControlSource) define bar lnBars + 1 of SHORTCUT prompt '\-' define bar lnBars + 2 of SHORTCUT prompt 'Find...' on selection bar lnBars + 2 of SHORTCUT ; loObject.FindField() endif .lFind ...endwith

That’s it for the strategy part. You can easily addadditional menu items if you wish. Adding them toSFTextBox.ShortcutMenu() means they’ll be available toall text boxes. You can also subclass SFTextBox and haveShortcutMenu() add additional items to the menu usingcode like:

dodefault()lnBars = cntbar('SHORTCUT')define bar lnBars + 1 of SHORTCUT prompt 'Whatever'on selection bar lnBars + 1 of SHORTCUT ; loObject.MyCustomMethod()

Page 17: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 17FoxTalk September 1997

You can also extend the behavior of SFTextBox bystoring a reference to an object with a ShortcutMenu()method into the oHook property; that object’sShortcutMenu() could add anything to the SHORTCUTpop-up.

Implementing the CalendarShortcutMenu() adds a Calendar bar to the shortcut menuif the textbox contains a Date or DateTime value.Choosing this bar calls the custom ShowCalendar()method of SFTextBox. Here’s the code for that method:

lparameters tnRow, ; tnCollocal loCalendarloCalendar = NewObj(This.cCalendarClass, This)with loCalendar .Value = iif(empty(This.Value), date(), This.Value) .Top = tnRow .Left = tnCol .Show()endwith

You’re probably thinking, “Wow, is that it?” Notexactly. There are several things happening here:

• SFTextBox has a custom property calledcCalendarClass. This property contains the name of aclass to instantiate for this calendar. By default,cCalendarClass contains “SFForms,SFCalendar” (I’lltalk about this in a moment). The advantage ofstoring the name of the class in a property rather thanhard-coding it into the method is that you can changethe class to use in the Property Sheet for a subclass ora specific instance of SFTextBox, or even change thevalue at runtime (say certain users get one type ofcalendar and others get a different type, or perhapsDateTime fields get a special dialog that includes ananalog clock).

• Notice there’s no CREATEOBJECT() command.Instead, I use a tiny program called NEWOBJ.PRG(included in the Subscriber Downloads) to create theobject and return a reference to it. NEWOBJ takes aparameter containing the name of the class toinstantiate, optionally preceded by the name of theVCX the class is in and a comma. NEWOBJ can alsoaccept additional parameters; these are simply passedto the Init() method of the object being created. Theadvantage of including the VCX name with the classname is that you don’t have to worry about whetheryou’ve done a SET CLASSLIB to the particular VCXor not; NEWOBJ will take care of that detail for you.

• Because cCalendarClass contains“SFForms,SFCalendar”, we obviously must have aclass library called SFFORMS.VCX that contains aclass called SFCalendar. This VCX is included in the

Subscriber Downloads. I won’t bother going over thedefinition of this class other than to say that it’s amodal dialog containing a Calendar ActiveX control(one of the ActiveX controls that comes with VFP 5)and OK and Cancel buttons.

• After the SFCalendar object is created, its Value is setto the date contained in the SFTextBox (or the currentdate, if nothing was entered yet), and its Top and Leftproperties are adjusted so the dialog appears near thetextbox that displayed it. The calendar is thendisplayed using its Show() method. SFCalendar hascode that puts the date chosen by the user in thecalendar back into the Value of the SFTextBox whenthe user double-clicks on a date or chooses OK.

Implementing the find functionalityShortcutMenu() adds a Find bar to the shortcut menu ifthe textbox has a ControlSource and its custom lFindproperty is set to .T. (which it is by default, meaning anySFTextBox bound to a field in a table or view willautomatically have find functionality). Choosing this barcalls the custom FindField() method of SFTextBox. Here’sthe code for that method:

local loFindwith This loFind = NewObj(.cFindFieldClass, .ControlSource, ; Thisform) loFind.Show(1)endwith

As with ShowCalendar(), FindField() is deceptivelysimple. It uses NEWOBJ.PRG to create an object of theclass specified in the custom property cFindFieldClass(which contains “SFForm,SFFindFieldForm” by default),passing to the new object the ControlSource for the textbox (so it knows what field to search) and a reference tothe form the text box sits in (so it can refresh the formwhen a record is found). As with SFCalendar, I won’tshow the code for SFFindFieldForm (you can check it outyourself, since it’s part of the Subscriber Downloads), butI’ll briefly describe it:

• SFFindFieldForm consists of three objects: Find Nextand Cancel buttons and a container object calledSFFindField (defined in SFCCTRLS.VCX).SFFindFieldForm accepts two parameters: the aliasedname of the field to search in and a reference to theform to refresh when you find a matching record.These two parameters are simply stuffed into customproperties of the SFFindField object, which has almostall the functionality of SFFindFieldForm.

• SFFindField is a container object with two controls: atextbox where the user can enter the value to search

Page 18: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

18 http://www.pinpub.comFoxTalk September 1997

for, and an option group where the user can indicatewhether the value should be found at the start of afield or anywhere within the field (as you canprobably guess, we’re going to do something likeLOCATE FOR &lcField = lcValue or LOCATE FORlcValue $ &lcField, depending on how the optiongroup is set).

• SFFindField has a custom FindNext() method, whichis called by the Find Next button in SFFindFieldForm.FindNext() searches in the specified field for the valuethe user entered and refreshes the form if a match isfound. It stores whether it found a match or not in acustom lFound property. If lFound is .T. (meaning amatch was previously found and the user wants tofind the next one), FindNext() uses CONTINUE. Ifnot, it uses a LOCATE FOR, as described in theprevious point. Thus, it acts likes the Find Nextbutton in Find dialogs such as those in VFP andWord; pressing Find Next finds the first match, andcontinuing to press Find Next cycles through allmatching records.

Why did I create an SFFindField container only todrop it on a form? Why not just put the controls directlyonto SFFindFieldForm? The reason is that I can envisionother uses for SFFindField. For example, I might want tohave a Find function for the form. Perhaps it brings up adialog containing a combo box of fields the user cansearch on and a SFFindField object, in which the user canspecify the value and how to search. Since the searchingcapability is encapsulated within SFFindField, all this newdialog needs to know is that it puts the name of the fieldchosen in the combo box into the cField property of theSFFindField object and calls the FindNext() method whenthe user clicks on a Find Next button.

You could improve upon SFFindField by supportingSEEK rather than LOCATE FOR. I chose not to becausethis would complicate matters; you’d likely have to addproperties to SFTextBox such as cSeekExpression (sincethe index expression for a field might not be just the fieldname, but something like UPPER(<field>)) and cTag (thename of the tag to search on), and set them for each fieldon a form. Remember, one of my design decisions wasthat it “just kicks in” without having to set any customproperties of any object on any form.

Every user I’ve demonstrated this feature to thoughtit was very cool, because the user interface is so simple:right-click on a field, choose Find, enter a value, and clickon Find Next to bring up records containing that value inthat field one at a time. Developers like it even morebecause with no effort (once the changes are made toSFTextBox and the other classes are created), you can addthis feature to all your existing forms. It just kicks in!

ExampleThe TESTFORM form in the Subscriber Downloads is avery simple form; it just has a few fields from aPRODUCTS table. However, right-click on any field andcheck out the Cut, Copy, Paste, Clear, and Select Allfunctionality. Choose Find and see how the find featureworks. Right-click on the Last Order date field and chooseCalendar to see how the calendar works.

To create the form, I simply set SFTextBox as thecontrol to create for certain data types in the FieldMapping page of the Tools Options dialog, dragged somefields from the DataEnvironment to the form, and createdsimple Previous and Next buttons. No custom propertiesto set, no custom code to write. It just . . . okay, I think youget the point.

SummaryThe changes we made to SFTextBox are summarized inTable 1. Of course, you don’t have to use SFTextBox; youcould apply these same changes to your own TextBoxbase class.

Table 1. Changes to SFTextBox.

Method/Property Change or PurposeRightClick() calls This.ShowMenu()ShowMenu() custom method to define, populate, and execute

a shortcut menuShortcutMenu() custom method to populate the shortcut menuShowCalendar() custom method to instantiate a calendar classCCalendarClass custom property containing the name of the

calendar classFindField() custom method to instantiate a find classCFindFieldClass custom property containing the name of the

find classLFind custom property; .T. if the user can find on this

fieldoHook custom property containing a reference to a

hooked object that extends the shortcut menu

I hope this article inspires you to create your owntools, and that you find the calendar and find features aspowerful yet easy to implement and use as I do. ▲

09HENNIG.ZIP at www.pinpub.com/foxtalk

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

Saskatchewan, Canada. He is the author of Stonefield’s add-on tools for

FoxPro developers, including Stonefield Database Toolkit for Visual

FoxPro and Stonefield Data Dictionary for FoxPro 2.x. He is also the

author of “The Visual FoxPro Data Dictionary” in Pinnacle Publishing’s

“The Pros Talk Visual FoxPro” series. Doug has spoken at user groups and

regional conferences all over North America and will be speaking at the

1997 Microsoft FoxPro Developers Conference. He is a Microsoft MVP.

[email protected], [email protected].

Page 19: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 19FoxTalk September 1997

The Progress MeterActiveX ControlJohn V. Petersen

ActiveX and OLE Automation Review FoxTalk

For two years, I’ve enjoyed writing the Dr. FoxPro’s AnswerClinic column. Beginning this month, my column undergoes afacelift, both in name and content. The new feature, “ActiveXand OLE Automation Review,” will be dedicated to exploring,in depth, two of the most popular technologies in theindustry today. Just as with the Dr. FoxPro’s Answer Cliniccolumn, if you have a particular question or would like to seea particular ActiveX control spotlighted, please drop me an e-mail at [email protected].

HAVE you ever wondered how to use an ActiveXcontrol that ships with VFP? Or, have youwondered if an ActiveX control for a particular

task exists? You might be wondering what’s involvedwith OLE Automation with products like Excel, Visio, andWord. As the number of applications that license VBAgrows, so too do the number of tools that VFP developerscan take advantage of.

Last month, Doug Hennig gave you a reusablethermometer class in his Reusable Tools column. Thismonth, I’m going to peek under the hood of the ProgressMeter ActiveX control that he used and discuss a coupleof ways to extend it.

One of the most useful and yet underused ActiveXcontrols is the Progress Meter Control. Unlike many othercontrols, the functionality of the Progress Meter Control isvery focused. Its sole purpose is to display a thermometerinterface to the user while a specific task is beingperformed. This ActiveX control allows VFP developers toimplement a thermometer dialog box that’s identical tothose found in most Windows 95 applications on themarket today.

Key PEMSThe three properties of most interest are Min, Max, andValue. As their names would indicate, the Min Propertyspecifies the minimum value the ActiveX Control can takeon, and the Max Value specifies the maximum value theActiveX control can take on. For example, let’s say the

Min Property is set to 0, the Max Property is set to 100,and the Value Property is set to 50. As you might guess,the progress meter would show a process being 50%complete. This is illustrated in Figure 1.

Figure 1 illustrates a sample form that manipulatesthe Min, Max, and Value Properties of the Progress MeterControl based on the values of the individual spinners.(This form, PROGSAMP.SCX, is available in theSubscriber Downloads.)

One of the nice things about the Progress MeterActiveX Control is that there’s no need to resort to anymathematics to determine how much of the thermometershould be filled. Based on the values of Min, Max, and theValue Property, the ActiveX control figures out how thedisplay should look. Another nice feature is that no matterhow the control is sized in terms of its width, the controlautomatically compensates.

Figure 1. By manipulating the Min, Max, and Value Properties, theappearance of the Progress Meter ActiveX Control will be altered.

Page 20: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

20 http://www.pinpub.comFoxTalk September 1997

Improving the interfaceWhile the interface for this control is very simple, it can beimproved. For one thing, there’s no method for refreshingthe display of the control. Additionally, there is nomethod for resetting the value to zero. Although it wouldonly be a matter of manipulating the Min, Max, and ValueProperties, it’s a cleaner design if a class has the innateability to manipulate its display. To augment thefunctionality of the BaseClass, I created the followingsubclass:

DEFINE CLASS oleprogress AS olecontrol

Height = 33Width = 263Name = "oleprogress"PROTECTED maxPROTECTED minPROTECTED value

*-- This method resets the Value Property to 0.PROCEDURE reset This.Value = 0 ReturnENDPROC

*-- This method accepts two parameters: nCurrentItem*-- (the current item being evaluated) and nTotalItems*-- (the total number of items to be evaluated). The*-- progress meter will be updated with the following*-- formula: (nCurrentItem/nTotalItems)*100 .PROCEDURE refreshdisplaylParameters nCurrentItem,nTotalItemsAssert Type("nTotalItems") = "N" And nTotalItems > 0Assert Type("nCurrentItem") = "N" And nCurrentItem >= 0

With This .Max = nTotalItems .Value = nCurrentItemEndWith

ReturnENDPROC

PROCEDURE InitWith This .Min = 0 .Max = 100EndWith

ReturnENDPROC

ENDDEFINE

With this simplified interface, the code to tell the ActiveXcontrol that it has a total of 100 items and is currently onthe 50th is as follows:

ThisForm.OleProgress1.RefreshDisplay(50,100)

What does the Progress MeterActiveX Control Lack?Many progress indicators have a numerical representationexpressed as a percentage of how much processing iscomplete. To add this functionality, a composite classbased on a container was created. The container has boththe oleProgress Class and a label class within its borders.Here’s the code for the Container class:

DEFINE CLASS progressmeterwithnumber AS container

Width = 346Height = 32BorderWidth = 0SpecialEffect = 1*-- Valid Values are 0 through 7.decimals = 0Name = "progressmeterwithnumber"

*-- Toggles the visible property of the pct complete*-- label.lpctvisible = .F.

ADD OBJECT oleprogress AS oleprogress WITH ;Top = -1, ;Left = 0, ;Height = 33, ;Width = 264, ;Name = "Oleprogress"

ADD OBJECT lblpctcomplete AS label WITH ;AutoSize = .T., ;FontBold = .T., ;Alignment = 2, ;BackStyle = 0, ;Caption = "Label1", ;Height = 17, ;Left = 271, ;Top = 6, ;Width = 40, ;ForeColor = RGB(255,0,0), ;Name = "lblPctComplete"

*-- This method calls the RefreshDisplay Method of*-- the embedded progress meter class.PROCEDURE refreshdisplaylParameters nCurrentItem,nTotalItemsAssert Type("nTotalItems") = "N" ; And nTotalItems > 0Assert Type("nCurrentItem") = "N" ; And nCurrentItem >= 0Assert Type("This.decimals") = "N"With This .OleProgress.RefreshDisplay(nCurrentItem,nTotalItems) .lblPctComplete.Caption = ; Str((nCurrentItem/nTotalItems)*100, ; 4+.Decimals,.Decimals) + " %"EndWith

ReturnENDPROC

*-- This method calls the reset method of the embedded*-- progress meter class.PROCEDURE resetThis.oleProgress.Reset()This.ResetLabelToZero()ReturnENDPROC

*-- Resets label to zero.PROTECTED PROCEDURE resetlabeltozeroAssert Type("This.decimals") = "N"Local lcDecimalslcDecimals = Space(0)If This.Decimals > 0 lcDecimals = "."+Replicate("0",This.Decimals)EndifThis.lblPctComplete.Caption = "0"+lcDecimals+"%"ENDPROC

PROCEDURE InitThis.lblPctComplete.Visible = This.lpctVisibleThis.ResetLabelToZero()ReturnENDPROC

ENDDEFINE

Page 21: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 21FoxTalk September 1997

In addition to the feature of numerically displaying thepercentage complete, some progress indicators also have afeature that will display the actual number of items thathave been processed (“Processing x of y”). To create thisfunctionality, the progressmeterwithnumber class wassubclassed as follows:

DEFINE CLASS progressmeterwithnumber2 ; AS progressmeterwithnumber

Width = 320Height = 68Name = "progressmeterwithnumber2"oleprogress.Top = 27oleprogress.Left = 3oleprogress.Height = 33oleprogress.Width = 264oleprogress.Name = "oleprogress"lblPctComplete.Left = 274lblPctComplete.Top = 34lblPctComplete.Name = "lblPctComplete"

ADD OBJECT lblprogress AS label WITH ;AutoSize = .T., ;Caption = "Processing x of y", ;Height = 17, ;Left = 15, ;Top = 8, ;Width = 94, ;Name = "lblProgress"

ADD OBJECT lblcomplete AS label WITH ;AutoSize = .T., ;Caption = "Complete", ;Height = 17, ;Left = 205, ;Top = 8, ;Width = 56, ;Name = "lblComplete"

PROCEDURE refreshdisplaylParameters nCurrentItem,nTotalItemsDoDefault(nCurrentItem,nTotalItems)This.lblProgress.Caption = "Processing " + ; Alltrim(Str(nCurrentItem)) + " of " + ; Alltrim(Str(nTotalItems))If nCurrentItem = nTotalItems This.lblComplete.Caption = "Complete"EndifENDPROC

PROCEDURE resetlabeltozeroDoDefault()This.lblProgress.Caption = Space(0)This.lblComplete.Caption = Space(0)ENDPROC

ENDDEFINE

Figure 2 illustrates all of these classes in action.

A word on designNotice that while there’s different functionality among thedifferent classes, the interface is constant. In other words,the method to refresh the display is alwaysRefreshDisplay(). This allows a developer to swap oneclass for the other without the need to change existingcode. To illustrate, the following lines of code arecontained in the Click() Method of the Start Button:

Local lnX,lnYlnX = 0lnY = Recc()Scan lnX = lnX + 1 This.Parent.OleProgress1.RefreshDisplay(lnX,lnY) This.Parent.ProgressMeterWithNumber1. ; RefreshDisplay(lnX,lnY) This.Parent.ProgressMeterWithNumber21. ; RefreshDisplay(lnX,lnY)EndScan

This form, PROGRESS.SCX, and the class library thatcontains these classes, OLECLASSES.VCX, are alsoavailable in the Subscriber Downloads. To runPROGRESS.SCX, copy the files from theVFP\SAMPLES\TASTRADE\DATA directory into thesame directory containing PROGRESS, and then run theform.

SummaryIn the columns that follow, the same formula will befollowed: an analysis of how ActiveX Controls and OLEAutomation work, how they can be extended, and lots ofsample code to speed up your development efforts. ▲

09PETERS.ZIP at www.pinpub.com/foxtalk

John V. Petersen, MBA, specializes in the development of FoxPro- and

Visual FoxPro-based solutions for corporate marketing departments.

John has written for several developer publications and has been a

speaker at user group meetings and developer conferences. He’s a

Microsoft MVP, a Technical Resource Specialist (TRS) on the

CompuServe VFOX, FOX, and FOXUSER Forums, as well as co-author of

Developing Visual FoxPro 5.0 Enterprise Applications from Prima

Publishing. 610-651-0879, [email protected].

Figure 2. Adding further functionality to ActiveX Controls is aseasy as creating a subclass.

Page 22: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

22 http://www.pinpub.comFoxTalk September 1997

Kit Box PotpourriBarbara Peisch

The Kit Box FoxTalk

But I didn’t want deleted recordsThere are some strange interactions with the SQLCOUNT() function and deleted records. The exactproblem has changed over the years, but it dates back to2.x and is still with us. I’ve gotten tips for this bugsubmitted by both Mark Nadig and Ilmar Waldner.

Run this code in various versions of FoxPro. The firstquery will always return the incorrect result. But the nextcase, where a field name is passed to the COUNT()function, will work in some versions but not in others:

CLEARSET DELETED ONCREATE CURSOR Test ( cTest C(10))INSERT INTO Test VALUES ( "One")INSERT INTO Test VALUES ( "Two")DELETE

* Incorrectly returns 2LOCAL aRet[1]SELECT COUNT(*) FROM Test INTO ARRAY aRet? _TALLY

* Sometimes incorrectly returns 2SELECT COUNT( cTest) FROM Test INTO ARRAY aRet?aRet[1]

* Correctly returns 1SELECT * FROM Test INTO ARRAY aRet? _TALLY

Note that deleted records appear when COUNT() is used,regardless of the setting of SET DELETED. The only wayto guarantee that your query results don’t include deletedrecords is to add AND DELETED() = .F. to the WHEREclause of your query.

How did my VCX/SCX get trashed?Chaim Caron has learned the hard way to make frequentbackups of his class libraries (VCXs and VCTs) andscreens (SCXs and SCTs). You might not realize it, but it’svery easy to get your classes or screens into a state wherethey won’t open and you can’t modify them. One thingyou might not consider when moving a class to anotherlibrary or renaming it is whether or not the class has achild class or screen. As the Class Browser help for theRename button says, this will “invalidate” the child classor screen. The easiest way out if you do this accidentallyis to restore the original class from a backup so you canmodify the child. You can also use the Redefine button inthe class browser to change the parent of a class or form.

The case has changed . . .This one is from Paul Maskens. Apparently, the SYS(1272)command respected case in version 3.0. In 5.0 that’s nolonger true. As an example, run the following code:

on key label F12 oRef = sys(1270)of = createobject("FORM")of.name="MYS004"of.show**** now point the mouse to the form and press F12? oref.name? sys(1272, oref)

In Visual FoxPro 3.0, the form’s name is “MYSOO4”; inVisual FoxPro 5.0, “mys004”. So, to make sure this onedoesn’t catch you, make sure you convert both sides ofany comparisons you’re doing involving the SYS(1272)function to uppercase.

. . . And the text has changed tooThis time the culprit is the PRMPAD() function, as MichelFournier found out. Once again, the value returned byversion 3.0 is different from what version 5.0 sends back.Consider the following line of code:

PRMPAD('_MSYSMENU','MYPADNAME')

In version 3.0, this would return the label with the hot keyidentified (that is, “\<MyMenu”). The same thing inversion 5.0 returns “MyMenu”.

Where did I put that Wait Window?If you use Wait Window commands for debugging, thenyou know that sometimes you can get a lot of them scat-tered in various methods. It’s easy to lose track of whichmethod you’re in when the window pops up. Instead ofjust displaying the value you need in the window, tryadding some more information. Here’s an example:

wait window "DEBUGGING:frmTeam.init: “ ; + “after select into aTeam: height of aTeam: " ; + str(aLen(aTeam,1))

This way you’ll not only know the value you’re lookingfor, but exactly which method and control you’re in.Adding the “DEBUGGING:” to the front helps later whenyou want to remove all your debugging windows. Justlook for DEBUGGING: with the Find feature of the editor,and make sure you click on the “All objects” option.

Continues on page 23

Page 23: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

http://www.pinpub.com 23FoxTalk September 1997

The Texas HeartLes Pinter

I’M lucky. I find things that no one else can find. Twoweeks ago, when I went panning for gold in theFeather River northeast of Sacramento with a friend of

mine, I found a quarter-ounce nugget. “Damn,” he said,“I’ve been prospecting for 25 years, and you find a nuggetlike that the first time out of the chute.”

But what I really wanted to find was a Texas Heart.Ying-Ying and I love to collect fossils, and I’d beenpromising her for six years that we’d go to the EdwardsPlateau in central Texas and find a specimen of Protocardiatexana—the Texas Heart, a 110-million-year-oldCretaceous pelecypod that would be the state fossil if theydidn’t have former senator John Tower.

Looking for fossils is like searching for the perfect de-sign element for an interface. You have a problem to solve,but the answer eludes you. You keep looking—magazinearticles, other applications, commercial software writtenin who knows what language. I invariably peek over thecounter in stores, embarrassing my lovely bride, to get alook at their point-of-sale screens in hopes of abscondingwith a good idea. Generally they’re some uninspiredmainframe junk that isn’t worth stealing. But I might finda good idea on the next screen, so I keep looking.

One of my users wanted pessimistic record locking. Itried to talk him out of it, because it’s more work thantheir application required. There was no way that twousers would ever work on the same record at the sametime. But he’d heard about it, and decided that this wasthe test of good software. Fortunately, I’d just found the

samples.vcx classlib in the VFP\SAMPLES directory. Itcontains a class that automatically does the entire job foryou—with no coding. I plugged it in, we edited the samerecord on two screens, it worked as advertised, and hewas satisfied and left me alone to get back to the reallyimportant stuff. I sure was glad I’d accidentally stumbledinto that class the week before. Like I said—I’m lucky.

I was in Austin last week at a client’s office. Ying-Yingand I drove down to Wimberly on Saturday, parkedbeside the Blanco River and started walking upstream,our eyes scanning every likely candidate. For hours wesearched, the hot sun baking our heads. I wasn’toptimistic, and although Ying-Ying never complains, Iknew that she would be disappointed—especially afterhearing so many stories of life in the shallow waters alongthe shores of the warm Cretacious sea that had built theancient reefs we were standing on. I had promised herthat we’d go to Texas and find fossils. Of course, we couldhave just gone to the Nature Company and bought one,but it wouldn’t be the same. I’d rather find it myself.

It was beginning to look like I would fail. Butsuddenly, I saw the telltale shape. I called to Ying-Ying.She squealed with delight, then bent down and turnedover the oval form. And there it was, a perfect TexasHeart—our reward for persevering in our quest. ▲

Les Pinter is the author of six books and over 250 articles on FoxPro. He

publishes a bimonthly newsletter, the Pinter FoxPro Letter. 415-344-3969,

fax 415-344-6026, [email protected], [email protected].

The Cutting Edge FoxTalk

Center groups of objects in 3.0Centering a group of objects on the same X-axis in VisualFoxPro 5.0 is easy. The “Center Horizontally” optionworks the way you’d expect. But in 3.0, this wasn’t thecase. Tim O’Leary has a workaround:

“Suppose you have a form with two buttons on it, OK andCancel, and you’ve centered them horizontally at the bottom.If you later change the width of the form, you’d like thebuttons to still be centered. You can’t select them and chooseFormat/Align/Center Horizontally, because they will end upone on top of the other. Instead, drop a rectangular shape onthe form and size it so that it just covers your buttons. Nowcenter the rectangular shape horizontally, and choose

Format/Send to Back so that it doesn’t cover the buttons.Now select both buttons and drag them to the center of theshape. Delete the shape and you’re done!”

One alternative to this method is to use a containerinstead, and place the buttons in the container. Make theborder of the container “None” if you don’t want a boxaround your set of buttons. You can then select just thecontainer and center it. This option avoids having tocreate and delete the shape when you want to center, butit does have a couple of drawbacks. First, it adds anotherlayer to the container hierarchy, which adds complicationsif you need to reference your buttons (such as withSetFocus()) from another object. Second, this isn’t practicalif there are lots of different things you want to center, andthe odds of having to do this multiple times are slim. ▲

Kit Box . . .Continued from page 22

Page 24: FoxTalk - Deutschsprachige FoxPro User Groupportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/ft0997_FT97i.pdf · FoxTalk Continues on page 3 Solutions for Microsoft® FoxPro® and

24 http://www.pinpub.comFoxTalk September 1997

Downloads

Portions of the FoxTalk Web site are available only to paidsubscribers of FoxTalk. Subscribers have access to additionalresources to give you an edge in FoxPro development.

User name

Password

endpoint

outer

GetWindowText accepts three arguments—the handle ofthe window, a pointer to the string containing the name,and the length of the string. To pass the second argumentas a pointer, use an ampersand after the data type in theDECLARE statement, and before the variable name in thefunction call. In a future column I’ll show you how to usefunctions like these to manipulate other applications,something that VFP doesn’t let you do simply by checkinga property.

Rules, rules, rulesOr perhaps I should say “rules, rules,” because there aretwo hard-and-fast rules for using the Windows API:Never mix data types, and don’t call functions that returnpointers. Because the Windows API is written in C andbecause C is a strongly typed language, you must use thesame data type specified in the documentation. VFP letsyou pass pointers to the Windows API, but it won’t returnthem. Just calling such a function can result in a GPF!Fortunately, such functions are the exception rather thanthe norm.

How do you find this stuff?Visual FoxPro 3.0 ships with the best documentation I’veever seen for the Windows API: a help file calledWIN32API.HLP. Unfortunately, Microsoft decided not toinclude this with VFP 5.0. This file describes functionsand structures, but because it’s written for Cprogrammers, you’ll have to know something about

function prototypes and structure typedefs to use it. I’ll becovering those topics in future columns.

WIN32API.HLP requires that you know the values ofthe many constants that it references. I’ve put most of theconstants in WINDOWS.H in the Subscriber Downloads.

Another excellent source of documentation onWindows API calls is included with Visual Basic. Nowthat VB and VFP are both part of Microsoft’s Visual Studiobundle, many of you have VB 5.0. The API Text Viewercontains all the function calls, constants, and structuresused by the Windows API. The Viewer is for VBprogrammers, of course, so everything is in VB syntaxand will have to be translated to VFP. This is pretty easy,though, because VB is closer to VFP than C is. The biggestdrawback to the API Text Viewer is that it only lists codewithout describing how to use it.

More to comeThere’s a lot to learn about the Windows API: structures,pointers, structures of pointers and devices. I’ll cover allof them in the next several months. Each month I’ll showyou how to do something cool, while incorporating all ofthat C esoterica a little at a time. If there’s somethingyou’re just dying to figure out how to do, let me knowwhat it is. ▲

09DEWITT.ZIP at www.pinpub.com/foxtalk

Gary DeWitt is senior software magician at Medsoft, Inc., a medical

practice management software company in Tahoe City, CA. While VFP is

his favorite development tool, he also programs in C++, Java, and Visual

Basic. Gary is a Microsoft Certified Professional in Windows architecture

and Visual FoxPro. [email protected], [email protected].

Calling All Windows . . .Continued from page 10

• 09SCHUMM.ZIP— A sample project and form showing how

to call MSINFO from a command button if MSINFO is

installed on the PC the form is being run on.

• 09DEWITT.ZIP—A sample WINDOWS.H file that contains the

values of constants used by VFP 3.0’s WIN32API.HLP file.

• 09HENNIG.ZIP—Sample classes (SFBUTTON, SFCCTRLS,

SFCTRLS, SFFORMS) and form (TESTFORM) for the Find and

Calendar functions in “It Just Kicks in.”

• 09PETERS.ZIP—This file includes PROGSAMP.SCX, a sample

form that manipulates the min/max and value properties of

the progress meter control, and PROGRESS.SCX, a sample

form and OLECLASSES that includes a numerical

representation of how much processing is complete along

with the graphical progress bar.

• 09PADDOC.ZIP—Sample programs and forms

demonstrating the instrumenting of an application.

September Subscriber Downloads