VFUG Newsletters
World Wide -- VFUG.Org

May, 2001

2001 May Newsletter -- VFUG

Ed. Spoke -- By Carl Warner

An Enhanced Collection Class, Part 1 -- J. M. Edgar

Where's the Marketing? -- Michael Asherman

Software Development Agreements -- J. M. Edgar, JD

Resources-- By Ed.
       o Visual FoxPro
       o MS KnowledgeBase
       o Various MS Links
       o .NET
       o XML
       o General Interest

Tips 'n Tricks -- By the Membership
       o GenLoader - A WSH Loader Wizard -- John Koziol/George Tasker Download Available
       o Benchmarking: Visual FoxPro 6.0 vs. Inprise Delphi 5 -- Ionut D. Baldazar
       o
Visual Class Navigators Library -- Alexander Khudyakov Download Available
       o How can I color code records/cells in a grid? -- Chris R. Chamberlain
       o Set Your Exe's Start Path On The Fly -- Ariel Gimenez
       o VFPLife.zip -- A VFP6 Mathematical Game -- John Koziol Download Available
       o Copying Files? -- Animated GIF Humor
       o Joke of the Month

ON SHUTDOWN....

Ed. Spoke

By Carl Warner

Yes, we are done accepting articles and tips for this issue of the newsletter.  Next month's will be coming along before we all realize it.  So, please contribute now for the June issue which will begin posting online on May 6th.

Within a week from now, we will have reached the 30,000 member plateau at VFUG.  That is positively awesome!  When I started being editor of this newsletter in October of 1997, we had just reached the 7,000 member count.  That's a lot of Fox developers and afficionados signing up.  We should all be very happy that we are definitely not alone in our interest of FoxPro, whatever version.

VFUG Support for Visual FoxPro -- Besides this monthly newsletter that is available online as it is being created, we have a few avenues offered up to every VFUG member, if they would just choose to use them.  We have an online threaded message forum that has gotten more use as of late, we have a list server that gets very little use by the membership, and we have a weekly online chat every Thursday that is only attended by a handful of the VFUG membership.  These are all ways to get questions answered, to lurk and learn something, or to show everyone how smart you are by answering the questions the rest of us can't explain to another member.  Most of you choose not to avail yourselves of these resources and, not only are you missing out, but so is the whole VFUG and Fox community by so many members not sharing information.  If you want to get all of your information from the software vendors, good luck in your developer career.

While many members think that sending me a message that says "unsubscribe" will get you removed from the mailing list, it will not.  Why not?  Because this newsletter is not on a list server, and it's availability is administered by you in your personal account information.  Select Members...Update Your Account Information (hiding on the bottom of the page), Mailing Option (toward the bottom of the page).  Then, don't forget to click on the button at the very bottom of that page to Save your account updates.

Now, on to the VFUG List Server which you can actually "unsubscribe" from if you just would "subscribe" to it to begin with.   This list broadcasts e-mail questions and answers into your mailbox without the use of a browser or having to consciously (or unconsciously) visit the VFUG website.   Many of you who have been on other list servers know how easy it is to sit back and receive e-mail all day without having to Browse for the same kind of Q&A somewhere on the 'net.  Plus, you generally setup a Rule in your E-mail reader to shift all incoming mail from any List Server to a specific e-mail folder so you can read the messages at your leisure.  How do you get subscribed? 

From the e-mail address you want to get VFUG Fox-related e-mails:

Setup to send a text e-mail message (no HTML or RTF)

address the e-mail TO:  ListServer@VFUG.Org

Subject doesn't matter, but you could type "subscribe" there.

in the BODY of the message: subscribe VFUGList <put your name here>

Example: subscribe VFUGList Dave Fulton

Then, send that message!

[You will get a confirmation whether it succeeded or not.]

Easy, right?   If you haven't done it yet, do it today.

As far as the VFUG Forum that is online at the site and accessible via a browser, visit there often to post questions, comments, or try to answers the questions of others.  But, I have to make some comments about that area right now.  Everybody involved in VFUG, whether it's you the membership or the officers, are not paid in any way.  All of this is a volunteer effort and any advice or help you get is purely out of the goodness of someone's heart.  So, what do we ask in return?  Do not demand help or criticize someone's effort to help you in any way.  Be polite and civil.  You are the one asking for free help.  If you get an answer that isn't exactly what you're looking for, ask again differently but don't be critical of the advice you just got.  If you get advice that seems to require that you spend a little money, it probably is because the solution you need will involve far more time and effort than anyone could possibly give you in the scope of a message or two in any online message.   We all don't have the time to do each other's programming work.  If you don't follow those simple guidelines, you probably will get your feelings hurt.  Treat others as you'd like to be treated and don't abuse your privileges for free advice.

Let's move on to other things.

It looks as though VFP7 will ship on or about June 6th, 2001.  That is according to what Amazon.com has posted for a ship date at their site.  You can pre-order the upgrade or the full package right now.  See below under Resources...Visual FoxPro for those URL links.

That leads us to the contents of this newsletter issue.  Since VFP has split off from Visual Studio and is its own little entity within Microsoft, most Fox developers are rightfully concerned about what will be done differently now with the product to help it to gain respect and sales in the general developer world.  Many of us know that the effort on the part of MS as a whole in bringing attention to VFP has not been where we'd like to see it.  What can you do as a developer to help send a message to MS about how you feel?  See the article in this newsletter entitled "Where's the Marketing?".  Read it and respond as you wish.  The article directs you on where to go on the 'net to get your voice heard within the collective Fox developer community.

Jim Edgar has two great and very different articles in this month's issue.  He graciously added a second article when he could tell that we were a little short on material.  Well, we're not even close to short now.  He first submitted an article on legal aspects of software agreements.  This is very good reading especially for all independent contractors doing software development.  The second article is the first in a two-part technical series on an enhanced collection class.  Check it out.

Also, in the Tips 'n Tricks section there are some great tips and downloadable files.  The downloadable files, as usual, are also posted in the VFUG File Archive.

Don't forget to check out the Joke of the Month that won't be available in the e-mailed text version.

And, lastly, there are more than enough internet resources for you to look through to find at least one or two that interest you that you weren't aware of.

The Hentzenwerke Publishing book on getting VFP certified shipped within the last week.  I just got a copy of the book on Friday and don't have the time to get a decent book review together for this issue.  But, I'll post a review on that book for the June issue and it will be available online, before the June e-mailing, as soon as I get it reviewed.  From my quick look at the book, it looks good.  But, it's way too early to comment in detail.

Keep hanging in there...

                          VFP Vendor                                                        Product or Service

F1 Technologies Visual FoxExpress Framework
ProMatrix Corp. Visual ProMatrix Framework
Recital Corporation Tools to get FoxPro Apps To The Web
Les Pinter Database Development Database Programming & Training Services
Walker Computing L.L.C. VFP Software Business For Sale

-- Ed.

Back To Table of Contents   Back To Top

At the VFUG Web Site:

VFUG Home Chat Real-time online chatting at least every week, usually on Thursday at 3pm EST, 2pm CST, etc.  Topics are sometimes announced but are open.
File Archive Fox-related files, utilities, & newsletter downloadables.
Lectures Online lectures, presentations, conferences. Support Use the VFUG Forum to post questions and supply answers to anything that is FoxPro-related.  Categories are clearly shown.
Knowledge Base Knowledge Base from our members.
Vendor List List of current and past Vendors of the Month. FAQ Frequently Asked Questions (FAQs) for the Fox community.
VFP Resource Center Tutorials are underway for beginners to MS Visual FoxPro.
Newsletters This area allows you to view all VFUG newsletters. Links This is an area that is constantly under construction as new web links of interest to the Fox community are posted.
Case Studies Case studies from our members.
Job Bank Job opportunities and jobs wanted postings. Members This area is where you can view the latest additions to the VFUG community as well as update your vital statistics including mailings.
   

VFUG Officers:

# Name VFUG Function E-Mail Address
1. John Chambers Public Relations Manager

John@VFUG.Org

2. Colin Keeler Web Manager

Colin@VFUG.Org

3. Carl Warner Newsletter Editor, Marketing Manager, Vendor Relations

Carl@VFUG.Org

4. Arnon Gal-Oz Technical Manager

Arnon@VFUG.Org

5. Tom O'Hare Operations Manager

Tom@VFUG.Org

Back To Table of Contents   Back To Top

An Enhanced Collection Class, Part 1

J. M. Edgar

Part 1 of a two-part series. This part addresses the requirements for and development of a collection class. 

In Part 2, the collection class will used to devise simple solutions to common programming problems.

A Collection is an ordered set of items that can be referred to and manipulated as a unit.  The items or members of a collection need only be related by the fact that they are in the collection.  Members in a collection do not have to share the same data type.  A collection is similar to an array, but unlike an array has built-in methods to manipulate its members.

Visual FoxProuses collections built into its foundation classes.  The _SCREEN has a Forms collection, a Form owns two collections: the Controls collection and the Objects collection.  The Pageframe has a Pages collection.  We can read the content of these collections, but cannot otherwise manipulate them.  Their usefulness is therefore very limited.

This article addresses the requirements for and creation of our own reusable collection class.  Next month, our collection class will used to solve some interesting ( at least I think they’re interesting) programming problems.

Collection Properties and Methods.

A collection class of some kind is available in almost development languages and has a well-understood set of properties and methods.

Items:            The collection itself is just a framework that holds content.  Content is arranged as discrete items (terminology varies considerably, so an “item” in one collection is a “member” or “element” of another even among Microsoft products).

An Item is nothing more than an addressable location within the collection where content is stored.  Items may be implemented as objects or properties of the collection.  The advantage of creating an Item as an object is that we can hang many neat properties on the object making it both powerful and flexible.  Most graphics collections, for example, define object items that contain information defining how a particular point is to be drawn.  The disadvantage of object items is that it is complicated to create an indexing object.   It is simpler to implement the item as an indexed property.  In FoxPro™ we already have a native indexed property, the array, so if we create our items as an indexed property, we can used the native array to greatly simplify our implementation.

Just as in an array, an integer index is used to point to the place in the collection where a particular content is stored.  Items are addressed as:  oCollection.Item[ n ], where n is an integer. 

An index may be zero-based or one-based.  In a zero-based collection, the first item in the collection is addressed as oCollection.Item[ 0 ] and the last item as oCollection.Item[ oCollection.Count – 1 ].  These indices are popular in development languages because they greatly simplify array mathematics. 

The FoxProstandard is the one-based index inherited from dBaseII.  The first item in the collection is addressed as oCollection.Item[ 1 ] and the last item as oCollection.Item[ oCollection.Count ].  A one-based index makes it easier to use a FoxProarray to store the items in an array without a lot of complex index management.

Items in a collection are addressed with the following syntax:  oCollection.Item[ n ].  In many collections, the “Item” portion of the syntax is a default property.  A default property is one that is assumed when omitted, so that the abbreviated syntax oCollection[ n ] is the equivalent of oCollection.Item[ n ].   In FoxPro, however, we will have to use the full syntax since the language makes no provision for default properties.

In some collection classes, items in a collection cannot be addressed directly, but must be addressed through a method.  For example, in Visual Basic, oCollection.Item( n ) is used to return the content of Item[ n ].  In our collection, we will give the user a choice of using the method oCollection.Value( n ) to return the content of Item[ n ] or to address the item such as uContent = oCollection.Item[ n ].

An Item in a collection usually contains just one data position.  But, it could contain many positions, each position holding content.  We would then have to use a second index to address the item such as oCollection.Item[ n1, n2 ] where n1 points to the Item and n2 points to a discrete position within the Item.  Single- and multiple-position items can be mixed in a collection.

Typically, any kind of data can be stored in a collection, and the data types may be mixed.  Indeed, an item may even contain a reference to another collection object, making multi-dimensional “collections” possible.

Count:           The Count (or Length) property specifies the number of items in the collection.  To ensure that our Count is always accurate, we will create and raise a Count_Access() event each time Count is read to recalculate the number of items.

Add():            The Add() method inserts an Item into the collection.  It typically has the following minimum syntax:

myCollection.Add( uContent [, nIndex ] ),

where uContent is the data to be stored in the item pointed to by nIndex.  If nIndex is omitted, the item is appended to the end of the collection.   There are many variations to the Add() method.  For example in Visual Basic the Add() method accepts parameters that indicate whether new item is to be inserted before or after an existing item in the collection.

 In our implementation,  we are going to make the Add() method also handle updates.  A collection class does not normally support an update process.  Items are “updated” by being removed, modified, then added back to the collection.  A collection class is much more useful, however, it its content can be updated.

Remove().     The Remove() method deletes an item from the collection.  For example,

myCollecton.Remove( [ nIndex ] )

removes the item at position nIndex.  If nIndex is omitted, the last item in the collection is removed.  In the standard collection, nIndex would always be a pointer to a location.  In our collection class, we are going to permit the user to also pass content as an argument to Remove() such as:

myCollection.Remove( uContent ).

where uContent specifies the content to be removed from the collection.  Remove() then locates the content in the collection and removes the item having that content.

Non-Standard Properties and Methods

The standard set of collection properties are very basic.  To make the collection more useful and flexible, a number of enhancements immediately suggest themselves.

It would be handy to be able to determine the item number at which a certain content is located, to be able to return the content at a location specified by an index and to completely remove all items in a collection with a simple process.   In our collection, these processes are implemented as follows:

Value( nIndex )        Returns the content of the item at nIndex.  This is similar to the Visual Basic Item() method.

Index( uContent )    Returns the index of the item containing uContent.

Clear()                       Empties the collection, and nulls any object references that were part of the collection.

Our only non-standard property is NewItemIndex.   It specifies as an integer the item index of the last item added to the collection.  This property is set in the Add() method.

So without further fuss, let’s code.

Creating a Collection Class

We will base our collection on the VFP custom class.  The custom class is a non-visible class that has few native properties or methods and therefore has a very small footprint and little object overhead.

Create a new custom class named “Collection” in the class library of your choice.

Add the Count property to the class and set it to zero.  This is the only property that must exist when the class is instantiated.  Otherwise, the Count_Access() event we are going to write below will sometimes not work properly (early- and late-binding issues).

We are going to add all other properties through code in the Init() event.  There is no reason these “internal” properties cannot be added to the property sheet.  It is generally good practice to try to minimize the number of properties appearing in the property sheet by including only those that may need to be set.  If the property is intended solely for internally use or does not need to be set in the property sheet, there is no reason to expose it in the property sheet for someone to fool with.

Modified Methods:

Init():            Add the following to the Init() event code of your collection class.  All this does is add two internal properties to the class.  Again, you may elect to add these to your class through your project manager.

*     Collection.Init()
*

*     Add internal properties to the class
*

WITH this
*     The array that holds the collection members.  This array is always initialized with
*     .NULL. that signifies to the Count_Access() event that the collection has no members.
      .AddProperty( "Item[1]", .NULL. )
*     Specifies the index of a new member added to the collection.  The index is a pointer
*     to the location of the member in the collection.

      .AddProperty( "NewItemIndex", 0 )
ENDWITH

RETURN .T.

Destroy():    Include the following code in the Destroy() event.  Destroy() calls the Clear() method of the collection just in case the members of the collection contain object references.  Any object references in the collection should be set to .NULL. or .F. when the collection is destroyed to prevent hanging references from clobbering your form release.

*     Collection.Destroy()
*
*     If the members are composed of object references, the Clear() method sets the
*     references to .NULL. before collapsing the Item[] array.  This eliminates any
*     hanging object references which may prevent a form from closing.

this.Clear()
RETURN

New Methods:

Add():   The Add() method handles all additions and overwrites.

The usual collection supports adding and removing items.  Added items are placed at the end of the collection.  There is usually no way to insert an item in the middle of the collection.  Similarly, once content is added to the collection, there is no means of overwriting the content.  The usual, if inelegant, solution is to remove the old content and append a new item with the revised content.

To make our collection more useful, we want to be able to both update existing content and insert a new item at any place in the collection.

We also want our design to recognize that some processes are used more frequently than others.  These processes in order of frequency of use are:

·                    Appending a new item to the end of the collection,

·                    Updating an existing item in the collection,

·                    Inserting a new item in the middle of the collection.

Our method design should implement the most frequently used process with the simplest syntax, adding to the complexity of syntax as frequency of use decreases.  With this precept in mind, the design requires the least complex syntax to append an item at the end of the collection and the most complex syntax to insert an item in the middle of the collection.  The Add() syntax is,

 Add( uContent [, nIndex1 ] [, nIndex2 ] [,lInsert ] ),

where,

uContent        Specifies the content to be added.  This may be of any valid data type, but it cannot be NULL.  NULL is used to indicate an empty collection.  Any attempt to add .NULL. to the collection is ignored.  If uContent is omitted, nothing will be added to the collection.

            nIndex1           Specifies the item to be added, inserted or replaced. 

If this parameter is omitted, zero or greater than Count, the new item will be appended at the end of the collection.  If its value is greater than Count + 1, the new item will nonetheless be appended at Count + 1.  Items must be added to the collection in sequence.  Skipping is not allowed.  Item 5 cannot be added before Item 4.  If there are two items in the collection, an attempt to add content at item 5 will actually cause it to be added as item 3.

If the nIndex1 points to an item that already exists, it is presumed that the content of the item is to be replaced. unless lInsert is true.

To find out where a content was actually added, test NewItemIndex that holds the index of the last item added to the collection. 

nIndex2            Specifies the position on the item were the content is to be added.  If the position does not yet exist, it will be created.  Positions on an item do not have to be filled in sequence.  Content can be added to position 3 before it is added to position 1 or 2. 

                                   If nIndex2 is omitted or zero, the content will be added at the first (default) position on the item. 

lInsert              If true, forces the insertion of a new item.  If omitted or false, the content of an existing item will be replaced with the new content.  If the item specified in nIndex1 does not exist, it will be added irrespective of the setting of lInsert, which is, in effect, ignored.

Here is the Add() code.  The comments explain what each section does.

*     Collection.Add()
*
*     OVERVIEW:   Add a new item to a collection object or replace an existing item with new *  *                  content.
*     PARAMETERS: tuContent   Specifies he content to be associated with the new item.
*                             May be any data type, but may not be .NULL.
*
*                 tnIndex1    Specifies position in the collection at which the new item is
*                             to be inserted or where existing content is to be replaced.
*                             If omitted, zero, or larger than the Count property setting,
*                             the item is added at the end of the collection.
*
*                 tnIndex2    Specifies the column in which the value is to be added.
*                             Required for multi-positional items.  Otherwise ignored.
*
*                 tlInsert    Specifies whether a new item is to be inserted into the
*                              collection.

*
*     RETURN:     (L)         True if the content was successfully added to the collection.
*

LPARAMETERS tuContent, tnIndex1, tnIndex2, tlInsert

LOCAL lnCount, lnNewCount, lnPosition, lnItems

WITH this 

*     Test and normalize parameters 

      IF PCOUNT() = 0 .OR. ISNULL( tuContent )
            *     The content to be added to the collection was not passed or passed as a
            *      .NULL. 

            *     Just exit.  Blank or .NULL. content cannot be added to the collection.
            RETURN .F.
      ENDIF     

*     Since reading Count triggers Count_Access() we do not want to read it over and
*     over
Read it once and store the result in a local variable.
lnCount     = .Count   
lnNewCount  = lnCount + 1

IF    PCOUNT() = 1 ;

   .OR. VARTYPE( tnIndex1 ) # “N” ;
   .OR. tnIndex1 < 1 ;
   .OR. tnIndex1 > lnNewCount
*     If the item index was not passed, is the wrong data type, is less than 1
*     or greater than Count + 1; execute the default operation, i.e. the new item is
*     to be added at the end of the collection.
        tnIndex1 = lnNewCount
ENDIF
*     Items must be added to the collection in sequence without skipping over
*     an item. The item index parameter cannot, therefore, be greater than lnNewCount.
*     If the item index is greater than lnNewcount, reduce it to lnNewCount.
*     The user may test NewItemIndex to find out where an item was actually added.
tnIndex1 = MIN( tnIndex1, lnNewCount )

IF PCOUNT() < 3 .OR. VARTYPE( tnIndex2 ) # "N”

*     Position index not passed for a multi-position item.  If the item to be
*     added or updated is a multi-position item, assume the content is to be
*     placed at position 1 of the item.  If the item to be updated is a single
*     position item, the content can be added only to the single position and
*     tnIndex2 is irrelevant.  Set it to 0 (zero) so lnPositions will be correctly
*     calculated below.

IF ALEN( .Item, 2 ) > 0
      tnIndex2 = 1

ELSE
      tnIndex2 = 0
ENDIF

      ENDIF

      *     Get the number of positions to be added to the items in a multi-position
      *     collection.  This value is the greater of tnIndex2 or ALEN( .Item, 2 ). If the
      *     item to be added or updated is a single-position item, lnPositions will be zero.

lnPositions = MAX( tnIndex2, ALEN( .Item, 2 ) )

       *     Find out if the content is to be inserted into the collection or replace
       *     existing content. 

      IF ( PCOUNT() = 4 .AND. VARTYPE( tlInsert ) = "L" .AND. tlInsert ) ;
            .OR. tnIndex1 > ALEN( .Item, 1 )
             *     If four parameters were passed, the 4th parameter is logical and true,  force
             *     an insert.
            tlInsert = .T.
            lnItems  = ALEN( .Item, 1 ) + 1
      ELSE
            tlInsert = .F.
            lnItems  = ALEN( .Item, 1 )
      ENDIF
       *     If the array does not already contain at least that number of columns specified
              *     in lnPositions, dimension the array to the number of columns specified in  
       *     lnPositions.

      IF lnPositions > 1 .AND. lnPositions > ALEN( .Item, 2 )
            DIMENSION .Item( tnIndex1, lnPositions )
      ENDIF
      IF tlInsert ;
                 .OR. ( tnIndex1 > ALEN( .Item, 1 ) ) ;
                 .OR. ( lnItems  > ALEN( .Item, 1 ) )
      *     If item[ 1 ] is .NULL., the collection is empty, so the new item
            *     will be Item[ 1 ], but if item[ 1 ] is not .NULL., then we must
            *     add an item.
               IF ISNULL( .Item[1] )
                                    *     The collection should have already been cleared if the Item[ 1 ]
                                    *     is .NULL.  But, in case it has not been, clear it now.
                  .Clear() 
            ELSE

*     We must add a blank row to the Item array before calling AINS(). 
*     AINS() does not re-dimension the array before inserting a new item. 

*     If the array is not re-dimensioned, the last item in the collection
*     will be lost when the new item is inserted.

                  IF ALEN( .Item, 2 ) > 0
                        DIMENSION .Item( tnIndex1, ALEN( .Item, 2 ) )
                  ELSE 
                        DIMENSION .Item( tnIndex1 )
                  ENDIF 

*     Insert an item into the array at tnIndex1.  The new item is
*     inserted just before tnIndex1.

                  AINS( .Item, tnIndex1 )           

            ENDIF

       ENDIF

      *     If this is a multi-positional item, tnIndex2 will not be empty.  Passing a non-
            *     empty tnIndex2 indicates that tuContent is to be added to the item at the
            *     position specified in tnIndex2.  Note that tnIndex2 = 0 is essentially the same
            *     as tnIndex2 = since in both cases the content will be inserted at position 1 on
            *     the item.

      IF !EMPTY( tnIndex2 )
            .Item[ tnIndex1, tnIndex2 ] = tuContent
      ELSE 
            .Item[ tnIndex1 ] = tuContent
      ENDIF

*     Save tnIndex1 as a property.  NewItemIndex is used to remember the location
*     of the last item added, inserted or updated.

      .NewItemIndex = tnIndex1

ENDWITH

RETURN .T.

Clear():      Clear() removes all items from the collection.  It takes no arguments.  In a “cleared” collection, the Count property specifies zero even though there is actually one (.NULL.) element in the Item[] array.   FoxPro™ does not allow an empty array   An array must contain at least one element.  Therefore, the Count_Access() method determines whether any items are in the collection by reference to the content of Collection.Item[ 1 ].  If it is .NULL. and ALEN( Collection.Item, 1 ] ) = 1, the collection is considered empty (Count = 0).

*     Collection.Clear()
*
*     Clears all of the items in the collection

WITH this
     *     As a precaution, nullify any object references that may be in the collection.
      DIMENSION .Item( 1 )
      .Item[ 1 ] = .NULL.     
ENDWITH 

RETURN 

Count_Access():  The Count_Access() event occurs when the setting of the Count property is read.  Count specifies the number of items in the collection.  Since the number of items is not always the number of elements in the Item[] array, some adjustment has to be made. 

A VFP array can never contain zero elements, but a collection may contain zero items.  Consequently, if the array contains one element, this event determines whether there are one or zero items in the collection.  Count is treated like a read-only property.  Its value cannot be set except in this method.  If it is set elsewhere, the setting is ignored. 

*     Collection.Count_Access()
*
*     Return the number of members in the collection.

WITH this
      *     If nothing has been added to the collection or all of its members have been
      *     removed, the value of .Item will be .NULL.

      IF !ISNULL( .Item[ 1 ] )
            .Count = ALEN( .Item, 1 )
            RETURN .Count
      ENDIF

ENDWITH 

RETURN 0 

Remove():   The Remove() method deletes a specified item from the collection.  An item is specified by passing either its index or its content.  It syntax is,

Remove( nIndex | uContent ), 

where nIndex specifies the item to be removed or uContent specifies the content of the item to be removed. 

If an index is passed, the item located at the position specified by the index is removed.  If content is passed, the item having that content is located and removed.  Content is not position-specific in two-dimensional arrays.  If the content is found in any position on an item, the item is removed.  If the content is contained in more than one item, the first item having the content is removed. 

If content is a number, it may look like an index.  The second parameter of the method sorts this out.  If the first parameter is numeric, it is assumed an index unless the second parameter is passed as true (.T.).   In such case, the number is treated as content and the collection is searched for the number. 

*     Collection.Remove()
*
*     OVERVIEW:   Used to remove an item from a collection.  The entire item is removed
*                  whether or not the item is a one- or two-positional item.

*
*     PARAMETERS: tuParm      The content of an value to be located in the collection OR an
*                              integer index specifying the item to be removed.

*
*                 tlSearch    Specifies whether a numeric tuParm represents the content of an
*                             member to be located in the collection or the index of an item to
*                             be removed.  If omitted or false, tuParm is presumed to be an
*                              index.

*                             If true, tuParm is content to be located in the collection. 
*                              tlSeach has no effect unless tuParm is numeric.
*    

*     RETURN:     (L)         True if the specified item is removed.
*

LPARAMETERS tuParm, tlSearch 

LOCAL ll2Dim, lnElement, lnItemIndex, lnElementCount, lnI

lnItemIndex = 0 

WITH this 

*     Get the number of elements in the array.  If we need to look for tuParm by
*     value, we will look at every element of the Item[] array then return a row
*     index
 using ASUBSCRIPT()

      lnElementCount = ALEN( .Item ) 

      *     Find out if this is a two-dimensional array.

      ll2Dim = ALEN( .Item, 2 ) > 0     

      DO CASE     

      CASE PCOUNT() = 0

            *     Index not passed.  Assume the item is to be removed from the
                        *     end of the collection

            lnItemIndex = ALEN( .Item, 1 )           

      CASE VARTYPE( tuParm ) = "O"

           *     If tuParm is an object reference to be located in the collection,
                      *     ASCAN() cannot be used because it will not accept an object as a
           *     search expression.

            FOR lnI = 1 TO lnElementCount           

                  IF ISNULL( .Item[ lnI ] ) .OR. .Item[ lnI ] = tuParm                 

                        IF ll2Dim

                              *     If this is a two-dimensional array, get the
                              *      “row”
that lnI belongs in from ASUBSCRIPT()

                              lnItemIndex = ASUBSCRIPT( .Item, lnI, 1 )

                        ELSE

                              *     If this is a one-dimensional array, lnI
                              *      already points to the correct position.

                              lnItemIndex = lnI

                        ENDIF                                   

                        EXIT

                  ENDIF

            NEXT                                   

      CASE ( VARTYPE( tlSearch ) = "L" .AND. tlSearch ) ;
            .OR. VARTYPE( tuParm ) # "N"

            *     tuParm is a value to be looked up.  If tuParm is found in the collection,
                        *     its row location is calculated and returned. 

            lnElement = ASCAN( .Item, tuParm ) 

            lnItemIndex = ;
                        IIF( lnElement > 0 .AND. ll2Dim, ;

                        ASUBSCRIPT( .Item, lnElement, 1 ), ;
                        lnElement ;

                     )

      OTHERWISE
            *     tuParm is a numeric row index
            lnItemIndex = INT( tuParm )
      ENDCASE    

      IF lnItemIndex > 0 

*     A FoxPro array cannot contain fewer than one element.  Consequently if
*     the last element is removed, the element count will still be 1, but the
*     value of the array will be .F.  We can’t tell whether this .F. is due to
*     removing the last element or if it is an actual value.  Therefore, when
*     the last element is removed from an array, set it to .NULL.

IF ALEN( .Item, 1 ) = 1
          *     We don’t have to write code to do this since the Clear() method
     *     will set the array to .NULL. 
      .Clear()
ELSE

                  *     Remove item from the collection.

                  ADEL( .Item, lnItemIndex )           

                  *     Redimension the collection to remove the last row that is now empty.
                  *     But, since an array cannot be dimensioned to zero rows, make certain
                  *     it will remain dimensioned to at least one row.

            IF ll2Dim
                  DIMENSION .Item( MAX( ALEN( .Item, 1 ) - 1, 1 ), ALEN( .Item, 2 ) )
            ELSE
                  DIMENSION .Item( MAX( ALEN( .Item, 1 ) - 1, 1 ) )
            ENDIF
      ENDIF     

            *     Adjust lnElementCount in case lnElementCount - 1 = 0.  This a little
            *     obtuse, but stems from the fact that an array cannot have zero elements
            *     in FoxPro™.
            *     If Item[] had one row, and one is removed, it would have zero rows,

            *     which it cannot have.  Thus since its minimum rows is one, if this row
            *     is removed, its row count is still one (albeit an empty row), so
            *     adjust lnElementCount to 2 so the test below works where the number
            *     of rows in the array used to be 1.

            lnElementCount = MAX( lnElementCount, 2 )

      ENDIF     

      *     If the item was removed and the collection resized, the current length
      *     of the collection will be less than the former length

      RETURN ALEN( .Item, 1 ) < lnElementCount     

ENDWITH 

Index():  Index() returns the item index of the first item having the content specified in its argument.  Its syntax is, 

Index( uContent  [, lCaseSensitive ] ), 

where

 uContent specifies the content to be located and

lCaseSensitive specifies whether a string content is case sensitive.  By default, the search is case insensitive.  Only if lCaseSensitive is true will case be considered searching for character content.  If the specified content is not found in the collection, 0 (zero) is returned.

*     Collection.Index()
*
*     OVERVIEW:   Locates a value in the collection and returns an integer representing
*                 a pointer to the position in the collection where the value was found.
*
*     PARAMETERS: tuValue           The content to be found.
*
*                 tlCaseSensitive   Specifies whether strings are to be matched by case.
*                                   Default is false (matches will be case-insensitive).
*                                   Has no effect on non-string content.
*
*     RETURN:     (N)   The pointer to the item in which the value was located.
*                       If the value is not in the collection, returns 0 (zero).
*
*

LPARAMETERS tuValue, tlCaseSensitive

LOCAL lnI, lnElement

WITH this

      *     If there are no members in the collection, return 0 since tuValue cannot
      *     possibly be in an empty collection.

      IF .Count = 0
            RETURN 0
      ENDIF

      *     Initialize lnElement to zero

lnElement = 0

*     Look for tuValue in the collection.  This search pattern works for one- and multi-
*     positional items.

      FOR lnI = 1 TO ALEN( .Item )
            DO CASE
            *     Case insensitive search for a string
                              CASE !( tlCaseSensitive ) ;
                                   .AND. VARTYPE( .Item[ lnI ] ) = "C" ;
                                   .AND. VARTYPE( tuValue ) = "C" ;
                                   .AND. UPPER(.Item[ lnI ] ) = UPPER( tuValue )
                      lnElement = lnI
                      EXIT
             *     Search for any other data type or case sensitive search for
             *     a string.  
                  CASE VARTYPE( .Item[ lnI ] ) = VARTYPE( tuValue ) ;
                                  .AND. .Item[ lnI ] = tuValue
                 lnElement = lnI
                 EXIT
             ENDCASE

       NEXT

      *     If this is a two-dimensional array, convert lnElement to a “Row” subscript.
            *     A member’s “position” in a two-dimensional collection is the row it occupies in
            *     the Item[] array.

IF lnElement > 0 .AND. ALEN( .Item, 2 ) > 0
      RETURN ASUBSCRIPT( .Item, lnElement, 1 )
ENDIF
RETURN lnElement  

ENDWITH

Value():  Value () returns the contents of an item at the location in the collection specified by indices passed as parameters.  The syntax is,

Value[ nIndex1 [, nIndex2 ] ), 

where nIndex1 points to the item and nIndex2 to the position on the item.  If the specified location does not exist, .NULL. is returned.

*     Collection.Value()
*
*     OVERVIEW:   Returns the content of the member at the position specified in tnIndex.
*
*     PARAMETERS: tnIndex1    An integer representing the row location in the collection
*                             of the value to be returned
*
*                 tnIndex2    An integer representing the column location in the
*                             collection of the value to be returned.  Applicable only
*                             to a two-dimensional collection.
*
*     RETURN:     (U)         The value of the member at the location specified by tnIndex,
*                             if found, otherwise .NULL.
*

LPARAMETERS tnIndex1, tnIndex2 

WITH this 

      *     Test for a valid tnIndex1

      IF VARTYPE( tnIndex1 ) = "N" ;

.AND. BETWEEN( tnIndex1, 1, ALEN( .Item, 1 ) )     

            IF EMPTY( tnIndex2 )

*     If this is not a multi-positional collection, return the content of
*     the the item tnIndex1.           

                  RETURN .Item[ tnIndex1 ] 

            ELSE
                  *     If this is a multi-positional collection, return the content of the
                                    *     position pointed to by tnIndex2 

            RETURN .Item[ tnIndex1, 1 ]     

      ENDIF 

ELSE                       

            IF VARTYPE( tnIndex2 ) = "N" ;
                            .AND. BETWEEN( tnIndex2, 1, ALEN( .Item, 2 ) )                       
                  RETURN .Item[ tnIndex1, tnIndex2 ] 
            ENDIF 

      ENDIF     

ENDWITH 

RETURN .NULL. 

Conclusion 

And that is our Collection class.  Pretty simple really. 

You could add any number of enhancements.  A couple that come to mind immediately are a Count property that also returns the number of positions on an item and a Sort() method to place objects in a collection in order.    Having an impatient attitude about tools,  I only build as much of the tool as I need right now.  If more is needed later, it can be built then.  So far, I have had no call for a Sort() method or Count property that returns item positions. 

I make constant use of collections to gather objects that I need to process as a group.   They permit me to devise rather simple, and sometimes elegant, solutions to problems that would otherwise be very difficult to solve even with extensive code.  

Part 2,  will describe the use of our new collection class to solve a couple of real-world programming problems.

The first is a solution to the hanging form.  A hanging form occurs when an object outside of the form owns an object reference to the form or some object on the form.  The form cannot be closed until all of those outside references are destroyed.  We can use a collection keep track of the external objects that own references to the form or an object on the form.  When the form or object is destroyed, it tracks down and eliminates all of these external references.  No more hanging forms. 

The second problem is the OLE (ActiveX) control that absolutely refuses to pay any attention to your LockScreen setting.  If you read George Tasker’s article Smoother (Virtual FoxPro User Group, June 2000 Newsletter), you know that an ActiveX control on a form is not actually on the form, but is a separate window of its own.   The form is a window, the ActiveX is a window.  So when the form’s LockScreen is set to true, the ActiveX control ignores the setting because it does not consider itself a part of the form’s window.   In Part 2 we will use a collection to facilitate preventing OLE controls from repainting while a form’s LockScreen property is true. 

Happy coding. 

J. M. Edgar has been a software developer in xBase languages since 1984. He can be contacted on the Universal Thread or at jmedgar@yahoo.com.  

Copyright © 2001 Jurix Data Corporation.  All rights under copyright reserved.

Back To Table of Contents   Back To Top

Where's the Marketing?

By Michael Asherman

With the release of Visual FoxPro 7.0 scheduled for "late this spring", it's reasonable to wonder when the VFP marketing will begin. Isn't it customary to initiate a marketing campaign in advance of a significant new release? Microsoft started marketing .NET almost a year ago, and it's not even scheduled to ship for months. VFP is more than ready to sell, but isn't it overdue for some marketing?

Why the sudden concern about marketing, or lack thereof, when VFP has survived without it for all these years? The main problem is that chronic neglect has taken its toll on the VFP job market, not just in absolute numbers, but relative to just about every other segment of programming. With the recent downturn in the economy, the VFP job market has nothing left to surrender. To make matters worse, Microsoft's announcement of its intention to remove VFP from Visual Studio has reinforced the general public's doubts about the future of VFP. In the absence of any positive spin from Microsoft on this piece of news, the VFP community faces a public relations disaster. If you've enjoyed making a living from your hard-earned FoxPro programming skills, you have plenty of reason to be concerned.

How could we hope to influence Microsoft's plans for marketing VFP, when years of previous efforts to do that have failed? We can influence Microsoft, and it will be easy if we go about it sensibly. The VB community didn't have to squawk for long to get Microsoft to alter its plans for VB.NET. Of course VFP doesn't have nearly the clout of VB, but we've got some things in our favor. We can be persuasive by sending a coherent message with such clarity and force that it simply can't be ignored. The case has already been made: we need very little from Microsoft and they have nothing to lose and much to gain by obliging us. If you haven't yet reviewed the online discussion that's been going on for the past couple of months, you might find it interesting to look over the VFP Marketing references outlined at http://www.ideaxchg.com/ix07/vm/_sys/toccontu.htm.

What remains to be done, and by whom? The simplest thing you could possibly do would be to make a wish, but that doesn't sound too promising. The second easiest conceivable action you could take would be to utter a single syllable: just say Yes. Let me add your name to the list of those who would consider signing an open letter to Microsoft encouraging them to do a better job marketing VFP. Easy enough, but if it sounds a little scary you should take some comfort in the knowledge that more than 600 people (including 7 Microsoft VFP MVPs) have already put their names on this list, which you can see at http://www.ideaxchg.com/ix07/bymda/mdav0016.htm.

The need for better marketing is an issue on which the VFP community is in virtually unanimous agreement, and by conservative estimates we are at least 100,000, maybe more than a million strong. That is a lot of people, considerably more than the total number of Microsoft employees worldwide. Is there really any doubt that Microsoft would be influenced by a very clear and reasonable request, a modest one at that, from so large a group of their own customers?

If you've taken the trouble to read this far, I'd like to thank you for your interest and finish with this small request: please vote. You can email your vote to me, Mike Asherman, at mda@ideaxchg.com, or use your browser to post a message at http://www.ideaxchg.com/ix08/d4post.htm, or enter your name on the Fox Wiki at http://fox.wikis.com/wc.dll?Wiki~WillMicrosoftMarketVFP. When you vote, indicate how you want your name to appear (pseudonyms are OK) and whether you'd like to include an optional email link. You are welcome to add your own comments on either the Wiki or IdeaXchg if you have the time, but in any case, please take only a single minute more to cast your vote, Yes or No.

Michael Asherman
mda@ideaxchg.com

Back To Table of Contents   Back To Top

Software Development Agreements

J. M. Edgar, JD

There is an ancient truism among attorneys that "An oral contract is not worth the paper it wasn't written on." a truism so old that it was first written in Latin.

And it is absolutely true.

Any lawyer will tell you that, at least in theory, oral agreements are enforceable in court, but in practice they seldom can or will be simply because it is almost impossible to prove the terms of an oral contract.  You say one thing, your customer says another.  Who is the judge to believe?

Like any other oral contract, verbal agreements to develop, modify or maintain software are practically worthless. If you are working under one now, you are relying solely on the good will of your customer to pay you. If he does not, there is probably little you can do except withhold the software not much help when it comes to paying the mortgage.

I am always surprised at the number of highly intelligent people who will commit a year of more of their time and expertise to a complex project on the mere hope they will be paid sometime in the future.

A written software development agreement is an admitted nuisance. We usually cannot write it ourselves, so we have to hire that most dread of all individuals, an attorney. This costs money and time. And, frankly, odds are that our customers will pay us and that all will go well with our current projects, so we will never actually need it.

Until we do.

When the customer claims he can cancel the project without notice, that he does not have to pay you for the four months of work you have already done, and that he can keep the part you have already delivered:  You will be very unhappy that you did not invest in an enforceable written contract with more reasonable termination and compensation provisions.

The written agreement is our safety net. We will usually not need a safety net, but when we do we will be thrilled to have one.

A properly written software development agreement defines the outer boundaries of correct behavior of the parties toward each other, helps prevent disputes, and if disagreements develop, provides a means of resolving them without getting the courts involved. If the dispute does end up in court, it defines for the court the legal duties required of us and our customers and clarifies who owes what to whom, making it much easier and quicker for us to get the compensation to which we are justly entitled (with suitable interest, of course).

Now that you are convinced that you need a written agreement, what you need to do is find a lawyer and get him or her to work. So there is no point in reading the rest of this article.

Well….actually, there are a few good reasons why you should.

First, you will want to know what to look for in a software development contract just to ensure that your attorney has actually covered all the necessary elements. Second, you will need to be prepared to discuss the provisions you want in your agreement no two agreements are the same (If they are, they shouldn't be). And third, as an independent businessman or -woman, you just need to know this stuff because this is the legal environment within which you conduct business.

Specifications

Exactly what is the application going to do? This is what the customer most wants to know. Specifications are the "builder's plan" of the project, the blueprint, the outline, the… well, you get it.

It is to everyone's advantage to be as specific as possible. The clearer the specifications, the less likelihood of misunderstanding, customer dissatisfaction and eventual litigation.

Not only is writing clear specifications a necessary part of the agreement, it is almost always our first opportunity to start analyzing and blocking out an application, dividing it into modules and roughing out a progress plan. It is surprising how many developers agree to develop an application with only the vaguest idea of what will be required.

Once the clearest possible specifications are drafted, including flow and process charts if warranted, it is time to review them with the customer. Clarify anything that is not understood, fill in any gaps, and make absolutely certain the customer understands what you propose to do before you drop a contract on his desk. That way there are no surprises.

There are probably as many ways of writing specifications as there are developers writing them, but they generally fall into one of three classes:

Statement of Work: This is the most general form of specification. It provides an overview of the work to be accomplished with limited functional grouping and virtually no technical discussion. This is most suitable for very simple projects or initial letters of intent.

Functional Specifications:  The most effective in my experience are functional specifications: clear non-technical explanations of what the application will do with no mention of how the application will do it. This format is the easiest for the customer to understand, and does not tie you down to a specific technical means which you might want to change later without having to renegotiate the contract.

Technical Specifications:  With technical specifications, you describe not only what you will do, but how you will do it. The technical specification is the most detailed design blueprint. It often follows acceptance by the customer of the functional design specifications and is included in the contract as an addendum to the functional design. When dealing with IS departments, technical specifications are often the only specifications that are acceptable.

Technical specifications take a lot of time to develop, and you should be paid under a design contract.  Never write a technical specification without payment on speculation that you will get the development contract.  You may not.  Even if you have a gurantee that you will get the contract, there is no guarantee that the development will ever take place. 

Irrespective of what form of specifications you use, the specifications should clearly set out what platform(s) and operating environment(s) the application will be written for and what access and support the customer will provide. If you do not limit the platform and operating environments, you may be presumed to have agreed to develop for all of them (more about this below). If you do not clearly spell out what support and access the customer will provide, you may be stuck with completing the application on time and on budget even if the customer does not provide them.

Once you and the customer agree to the specifications, they are usually attached to the written contract as addenda and referred to in the contract language such as ".. the terms and specifications set out in Appendix A and made part hereof by reference as if fully restated anew herein" or something like that.

Timetable

A small project may be done in one swoop, but a large project requires phasing. You agree to deliver a certain part of the project in working order at a certain time, and the customer agrees to pay you a specified amount at that time.

There are a number of advantages to phasing:

·                    You get paid in installments rather than at the end of the project.

·                    Problems can be identified early, and resolved.

·                    If the customer signs off on each phase as "satisfactorily completed", it is very hard for him to claim later that the your work was incomplete or not satisfactory.

·                    The customer's needs are likely to change as the application is developed and the parties are going to uncover previously undiscovered requirements which usually mean that the application will be modified. Developing in phases is a convenient way to build in checkpoints at which the requirements of the application are reexamined, specifications modified, the timetable and costs adjusted. Almost never are projects completed on the specifications they started with.

There are also a number of important rules to phasing:

·                    It is critical that the developer agrees to a reasonable delivery schedule. Almost every developer I know (including me) tends to underestimate the time required to code, debug and test an application. So be careful not to shortchange yourself.

·                    The contract should provide that phases can be aggregated so that if you do not require as much time as you estimated in one phase, the saved time can be added to another phase that has taken more time than you expected.

·                    There should be a margin of error of at least twenty percent for each phase and for the project as a whole -- more if you can get it -- especially if you are being paid on a time-and-materials basis.

·                    Timetables should always be identified as "reasonable estimates" rather than "binding commitments" by the developer.

Once the timetable is agreed to, it also should be incorporated in the contract either as an appendix or, since it is usually short, in the text of the agreement itself.

Payment

Development contracts typically specify one of two forms of payment.

The fixed price contract pays the developer a specified amount for the project (and each phase) no matter how much time it takes. This type of contract is usually viewed as most favorable to the customer since he is guaranteed a product at a certain price.

The other form is the cost-plus or time-and-materials contract in which the developer is paid for the time spent and actual costs of the project. The time-and-materials contract is usually regarded as most favorable to the developer.

As a practical matter, however, they are about the same. If we have done our specifications and timetable carefully, the project is probably going to take about as long as we initially figured and since even a fixed price contract is based on our best estimate of the hours required, the cost to the customer should be about the same either way.

Given a choice, I almost always go for the fixed-price contract. If you are a fast coder and can beat the "book" time, you will make more money maybe almost as much as your auto mechanic. Of course, you take the risk of an unforeseen problem that will require an inordinate amount of time to solve but often these situations are so gross that the contract can be renegotiated to cover them.

The payment schedule is usually written in the body of the contract.

Just Whose Code Is It Anyway?

Customers are often astounded to find that even though they have paid you a lot of money to develop their application, they do not own the code from which the application was compiled and linked. The source code and the application are two different things the kind of distinction that lawyers love to make even though they baffle the rest of the world.  But just as buying a car does not give you any ownership of the factory that made it, buying an application does not give a customer any rights to the code that made the application.

Nevertheless, you do not want to confuse your customers about ownership of the code. Confused customers are unhappy customers who do not pay. Get the issue of ownership in the agreement and settled up front.

How source code ownership works does not seem to be very well understood in the FoxPro™ developer community. But it is really quite simple.

You do not own just the code. Your ownership would then be limited to those electronic bits in your computer or the scratches on the paper that make up the physical code. You own more than that. The code is an intellectual property subject to copyright. What you own is the code and the copyright to the code. A "copyright" is just what it says: the "right" to say who can use or "copy" the code.  (For an good presentation of elementary copyright law in layman’s language, see US Copyright Office: Copyright Basics)

The very instant code is created, it is copyrighted. Contrary to what you may have heard elsewhere, we don't have to actually do anything to copyright code the copyright law takes care of it for us.  A copyright attaches to original writings immediately upon writing.

Not only is there an instant copyright, there is an instant copyright holder. That's the owner of the copyright, and that's us, the developers. Actually there are many copyrights, not just one, but unless you are going to sell one of them, you need not be concerned with the many ways lawyers can slice and dice a copyright into its various component parts. When the copyright attaches, you instantly own all of the many copyrights.

We may perfect our copyright, but we don't have to. Perfecting a copyright is no more than a legal formality for registering a copyright with the Copyright Office of the Library of Congress in such a manner that all persons are given notice that we own it. But we do not have to perfect a copyright to own a copyright that any federal count will happily enforce.

One of the purposes of a written agreement is to determine who will own the source code   that is, who will hold the copyright. There are various degrees of ownership from the developer wholly owns the code, to the customer wholly owns the code, and every possibility in between.

Some customers insist on owning the code, in which case our price for developing the application should be higher. In fact there should be a specific transfer of ownership to the customer in the agreement at a agreed price over and above the price for developing the application.

Other customers don't care to own the code. But even though the law provides that if the agreement says nothing about who owns the code, the developer owns it by default, there should be a provision to the effect that the developer will retain ownership of the code. That way there are no unhappy surprises.

Protecting Your Toolkit

If the customer is to own the copyright on the code, how do you protect your toolkit? You know, the framework, base classes, functions and procedures you developed over many years that you use as the foundation of every application you write.

Some or all of this toolkit will end up incorporated in the customer's application. If he owns the code, does he then own your toolkit?

Yes, he does, unless the agreement specifically says he does not. So be careful. If the customer owns your toolkit, you cannot reuse it in another project without the customer's permission. You probably do not want to sell all those years of development experience for the price of one application.

Make certain the agreement specifically excludes your toolkit and any modifications you might make to your toolkit while writing the application, listing in general the classes, functions and procedures included in the toolkit. Also ensure that as you are developing the application, toolkit items included in the application are identified as such and a copyright notice written in the code in a prominent place for example at the top of each object method and procedure library. Then keep a dated copy of the code just in case the issue arises later.

To permit the customer to use your toolkit in his application, the agreement should specify that the customer receives a non-exclusive license to use the toolkit items. Notice the "non-exclusive". If you give the customer an exclusive license, it’s the same as having sold the toolkit outright.

Platforms and Operating Systems

You write the application. It works perfectly. The customer is happy and everyone is all smiles. Then the customer changes from a Novell server to an NT server. It does not work anymore. The customer wants you to come fix your "bug" for free, of course.

The contract should clearly inventory the customer's platforms and operating systems and specifically identify the platforms and operating systems on which the application is required to perform.  If the customer "upgrades" to Windows™ 2004, it should be clear that any modification to the application to accommodate the new operating system is additional work, and will required additional payment.

Which brings us to the subject of …

Warranties

Our customers would like us to guarantee that our applications will work perfectly forever – a little more warranty than we are likely to give.  But giving some level of warranty is inevitable.

I have always worked on the basis of reasonableness. If the customer has paid me a lot of money for a complex application, I should expect to warrant it for a year or even longer. If he has negotiated me down to bare bones, I am not going to be quite so generous.

Actually the software agreement should provide a number of warranties. The Performance Warranty is the one we are talking about now. This guarantees that the application will work per the contract specifications and if it does not we will fix it for free or for a very minimal payment. This is the warranty that incorporates the term certain: 90 days, one year, or whatever time is reasonable.

But there is also the Warranty Of Ownership which states that the customer will get good title to the application. If you have given a security interest to your bank in your work-in-progress to secure an operating loan, you may have to get a release from the bank in order to give the customer good title. This is the same warranty you got on your house. The seller warranted in the deed that he was giving you good and unencumbered title.

The Warranty of Non-Infringement says that in developing the application you have not violated anyone else's copyright, trade secret or patent. So if you used the framework you appropriated from your old employer, be careful. If the employer sues your customer, you will be paying the customer's cost of litigation as well as any damages the court may award to your old employer and any damages awarded to the customer for your breach of contract as well as your own legal costs. This could get expensive very quickly.

These warranties have no term. They are forever.  Furthermore, even if they are not included in the software development agreement, they may attach to the agreement as a matter of law in your state as implied warranties - in fact, they probably do. So, to make certain the state does not write them for you, ensure your lawyer does.

Other implied warranties should be specifically excluded. These are the Implied Warranty of Fitness For Purpose and Implied Warranty of Merchantability. Your performance warranty largely duplicates these, but since these warranties come with a boatload of historical clutter, they are a mine field for the unwary.

The Warranty of Fitness may be a special problem. It holds that if you knew or could reasonably be expected to know your customer's purpose in contracting for the software, you may be liable if the software does not fulfill his purpose. This liability may attach even if your work fully meets contract specifications and even if the customer’s purpose was never included in the contract. Ouch!

Some states do not permit these warranties to be excluded from consumer contracts, but most state laws have no problem with excluding them from commercial contracts between businesses. That's us commercial contracts between businesses. But if you do not exclude them, they are ordinarily included as a matter of law. So make sure they are out, out, out.

Dispute Resolution and Enforcement

One of the principal purposes of a written software development agreement is to head off disputes. But if a dispute arises, the well-written agreement generally provides a means of settling the dispute that does not involve the time and expense of going to court. These are called alternative dispute resolution provisions and generally fall into two classes, arbitration and mediation. They both have the twin advantages of being quick and much cheaper than litigation.

Arbitration  is the more formal of the two. An arbitrator acts like a judge, taking evidence from both sides (usually in fairly informal process) and giving a decision. Arbitration can be binding or non-binding. If it is binding, the parties agree to abide by an decision of the arbitrator. If it is not binding, then either party may disregard the decision and sue. But there is usually a penalty clause to the effect that if a party sues and loses, he pays the winner's attorney fees and other litigation costs.

Mediation is the process of resolving a dispute through an objective, neutral facilitator who attempts to aid the parties in reaching a settlement. A mediator has no power to make an independent decision on the issues. He merely assists the parties in reaching a fair accommodation. Obviously this works best if the parties are still being reasonable. When they stop being reasonable, someone might as well go ahead and sue.

Whichever you choose, agree on an arbitrator or mediator in advance and identify the person or organization in the contract. Otherwise, the first thing you may disagree about if a dispute arises is who the arbitrator or mediator will be, then you are only one short step from the courtroom.

Avoid the boilerplate arbitration clause where you pick an arbitrator, the other side picks an arbitrator, then the two of them pick a third arbitrator. If you are going to pay for the time of three professional arbitrators, you might as well just go to court.

Most larger communities have formal arbitration/mediation organizations, many sanctioned by the American Arbitration Association. There are also local and regional associations which can provide referrals to arbitrators and mediators who are often retired judges of some distinction. Actually, it is often possible to get a better, more experienced judge by going to arbitration than by going to court.

The Boilerplate

Everyone makes fun of boilerplate clauses in contracts. They run on and on and are utterly incomprehensive to anyone except lawyers and judges. But boilerplate is both useful and important. The clauses are the distillation of the experience of thousands of lawyers over hundred of years who have found that these specific provisions in a contract tend to produce an expected outcome if the contract is litigated. As for being incomprehensible: if it gets to the point where these clauses are important, you are in a lawsuit and the only person who really needs to understand them is the judge and he or she will.

Some boilerplate clauses are important and should be in every software development contract.

The Whole (Entire) Agreement Clause: This clause says essentially that the written contract (with attachments, modifications, amendments, etc.) is the "whole agreement" and “understanding” between the parties, and that there are no other documents or oral understandings that are part of the agreement. This prevents the situation where the customer admits that the contract states that he must pay you, but then says that you later told him over drinks that he really did not have to. What you may or may not have told him is not relevant because it is not part of the written contract. The written contract alone states the "whole and entire agreement" of the parties.

The Severability Clause: Your clever lawyer inserted a clause to the effect that in the event of a breach of contract by the customer you get to sacrifice his first born to the Great and Powerful Oz. Any court is going to find that clause void as against the rather sound public policy prohibiting human sacrifice. But you don't want the rest of the contract to be thrown out with it. Hence the severability clause which says that if one part of the contract is found to be void or unenforceable, it is severed, and the rest of the contract remains valid and enforceable according to its terms.

Choice of Forum/Choice of Law: You live in Peoria, the customer is an Alaskan business corporation domiciled in Icefloe, Alaska.  If you go to court, you certainly do not want to go to court in Icefloe, you want to go to court in Peoria. Choice of Forum/Law clauses state that in the event of a lawsuit, the suit must be brought in Peoria and the contract interpreted under Illinois law, not the Native Custom applicable in Icefloe, whatever that is.  If your customer is a foreign business, it is even more important that you choose law in some United States jurisdiction.  You do not want to go to trial in Bejing or Katmandu.

The "No Partnership" Clause: Your customer goes bankrupt and a lot of stiffed creditors are looking around for a deep pocket. They spot you, a successful and wealthy software developer. They sue you, claiming that the software development agreement you entered into with customer created a “joint venture” and that as a joint venturer with customer you are legally responsible for his debts arising out of the venture. Whoa, Nellie! Bet you never saw that one coming.

To eliminate this real possibility, the agreement should contain a clause that unambiguously identifies you as an "independent contractor" and not "a partner, employee, agent or joint venturer" of the customer, and should clearly state that "neither party is responsible for the debts or obligations of the other".   This clause is also helpful if the IRS comes poking around and wants to reclassify you as an employee of your customer to void all those nice business deductions you claimed on your Schedule C (even the ones that were actually true).

Terms and Definitions Clause: Sometimes called the "Interpretation" clause, this is the famous "up shall mean down" clause where every word in the English language seems to have been given another, different and obscure meaning.

Actually the clause is both useful and necessary. Lawyers use words that look and sound like English words but are not English words. They are legal terms of art that have a very specific meaning, unlike English words which may have many shades of meaning. Terms of art are just shorthand permitting a lawyer to use one word rather than a whole phrase to impart precision to a document. It is necessary for a contract to distinguish between a word used as an English word and the same word used as a Legal word. This is the clause that does that.

It also is the clause that defines terms such as "we" when used in a contract. If there are two parties to a contract, which one is "we"? Without the definition contained in this clause, it is often impossible to know.

One word of caution, though. Terms and Definitions Clauses are often the most boilerplate of boilerplate. Many attorneys adopted a favorite terms and definitions clause while still in law school and have not actually looked at it since. Therefore you should. If you find the word "Client" defined but it is the word "Customer" that is used throughout the contract, point it out. Poorly drafted terms and definitions clauses can get you in trouble the courts will use them exactly as written, so you'd best see they mean what you intend.

Written Modifications Clause: This is the clause that provides that the contract may only be modified "in a writing signed by both parties". It means what it says. Oral modifications have no effect. Get any changes in writing signed by the customer. The abuses this protects against are self-evident.

Finding and Hiring a Lawyer

So now you know what goes into a software development contract. Do you know enough to write your own? Probably not. So you need a lawyer. How do you find one?

The kind of lawyer you need is an intellectual property lawyer (sometimes called a “patent and trademark lawyer”). These are the guys and gals that specialize in contracts for non-tangible property, copyright registrations and often patent applications. In larger communities they are in the Yellow Pages. In smaller communities you may have to (1) go to a larger community or (2) search the Web.

The most common resource lawyers use for finding other lawyers is the Martindale-Hubble Directory which can be found on the Web and in most law libraries. The on-line version is most up to date. Be aware, however, that lawyers pay to get into Martindale-Hubble and their qualifications to do the kind of work advertised are sometimes aspirational rather than real, and not always thoroughly checked.

After you have found a lawyer, how do you make certain he or she is qualified?  Like any other trade or profession there are good lawyers and poor lawyers, and lawyers that are good at some things and lousy at others. The best way to hire a lawyer is the same way you hire a plumber: interview, ask questions, and check references.

Lawyers, who generally consider themselves a small step above plumbers, are loathe to give references. But if you insist, you will get them. Note carefully: if a lawyer tells you that giving out the names of his clients is a violation of attorney-client privilege, stand up and walk out. He just lied to you, and there is no reason to believe he will not continue to lie to you. But no ethical lawyer will give out a client's name without checking with the client first. So do not expect to get references immediately.

When the contract is done, read it. If you do not understand any part of it, have the attorney explain it until you do. By the way, if the lawyer can explain it in English, he or she can probably write it in English. Insist on it where possible. Unless your customer has his own lawyer, you may end up being the one who explains it to your customer. It's a lot easier to explain English that it is to explain Legalese.

Writing it Yourself

So you are one of those brave and hardy souls who is willing to take big risks – sky-diving, para-sailing, formula one racing, cliff diving. Well, you'll probably be as successful at writing a contract as a lawyer would be writing a FoxPro™  application. 

But before you touch that keyboard, try one of the on-line legal document sites. One or two are not bad. The best for this kind of document may be QuickForms which has fairly good standard software development, modification and licensing agreements for about $20.00 each. Its response is quick, about one minute after your credit card is accepted. These boys do not dither around.

If you are bound and determined to write a software development agreement yourself, at least start with the QuickForm document, then modify it to your heart's content. It does not, by that way, meet all the criteria set out above. But it is much, much better than no agreement at all.

And good luck.

J.M. Edgar
mailto:jmedgar@yahoo.com

J. M. Edgar
is a member of the State Bar of California, the Bar of the Federal District Court for the Eastern District of California, and of the United States Tax Court. He received his legal training at the University of Nebraska, McGeorge College of Law and Georgetown University School of Law.  He received a Juris Doctor in1980. A software developer in xBase languages since 1984, Mr. Edgar writes about and consults on the legal issues of software development. He can be contacted at on the
Universal Thread or at jmedgar@yahoo.com.

This paper is not intended as legal advice nor does it constitute the practice of law.  The information provided is believed to be accurate, but the author assumes no liability for its accuracy, currency or timeliness.  This paper does not necessarily describe the applicable law in alien lands such as Canada or Louisiana.

Copyright © 1993-2001 Jurix Data Corporation. All rights under copyright are reserved.

Back To Table of Contents   Back To Top

RESOURCES

By Ed.

Visual FoxPro

VFP Life -- a VFP6 Mathematical Game John Koziol Freeware
Order Microsoft Visual FoxPro 7.0 -- Full Version (U.S. only) Amazon.com
Order Microsoft Visual FoxPro 7.0 -- Upgrade Version (U.S. only) Amazon.com
Documentation templates for database projects dbtemplates.com
Whither Our Fox-Part 2 FoxTalk March 2001
InstallShield Express Visual FoxPro Home InstallShield.com
Visual FoxPro 6.0 Common Questions MS Shop
FoxLock Product Information eim software
ProMatrix Source Control Kit ProMatrix
An Autoincrementing Class Les Pinter Learning Center
Using ComboBoxes and ListBoxes Les Pinter Learning Center
MSDE Setup Fox Wiki

Back To Table of Contents   Back To Top

Microsoft KnowledgeBase

HOWTO Determine if a Service Exists Using Visual FoxPro
HOWTO Find Installed Modems with Visual FoxPro
INFO ILOVEYOU Virus Attacks FoxPro SCT Files
HOWTO Find the Day that Daylight Savings Time Occurs
How to Hide the FoxPro Run Command Window in Visual FoxPro
HOWTO Use the Windows Scripting Host to Automate an Application
HOWTO Create an Application Role on Microsoft SQL Server 7.0
HOWTO Set up a Visual FoxPro Internet Server
Vfp6int.exe VFP 6.0 ODBC Driver FoxPro.int Resource File
Vfpcom.exe Using COM Language Enhancements in VFP 6.0
HOWTO Set Up Source Code Control with Visual SourceSafe
INFO Time Difference in Checking Out vs. Getting File in SourceSafe
HOWTO Use OLE Automation with Visual SourceSafe
HOWTO Use a Project and File Object's SCC PEMs
HOWTO Synchronize a SourceControlled VFP Project Between Users
INFO Searching FoxPro Newsgroups
HOWTO Start Internet Explorer and Navigate to a URL Using OLE Automation
HOWTO Save and Restore Grid Column Order and Widths
How to Set Up Relationship in Data Env for Concatenated Fields
WhatsThisHelp.exe Implement HTMLHelp and WhatsThisHelp Concurrently
Example of Polymorphism in Visual FoxPro
INFO Description of the FOXTOUCH Function
Benefit of Storing Printer Info in a Report Table (.FRX File)
How to Access Report's Data Environment While the Report Runs
How to Modify a Report Preview Window Property
HOWTO Use Offline ADO Recordsets in Visual FoxPro
HOWTO Use COMCLASSINFO(2) to Check Installed ADO Version
HOWTO Simulate a Detail Band Longer Than One Page
INFO FoxPro Header Check to Determine if Table is Corrupt
INFO Programming in Reports w- Band Expressions on Entry-Exit
HOWTO Find the Full Path of an Executable Given the Extension
HOWTO Add Records with Memo Data Using the Visual FoxPro ODBC Driver
HOWTO Set Where .tmp Files Are Created with the VFP ODBC Driver
BUG Parameter Placeholders with Visual FoxPro (VFP) ODBC Driver and Large Tables
BUG OLE SUBCLASS Subclassing of OleControl-OLE Unspecified
BUG Fatal Error When Releasing a Converted FoxPro 2.x Form
BUG Visual FoxPro May Ignore DDE Messages
PRB Error Cannot Update the Cursor When You Modify Visual FoxPro Tables
PRB No Version Info if COM DLL-EXE Is Built Under Windows Me-98-95
PRB Application Appears Then Exits to Operating System
PRB Scrolling Message Record not available ... Please Wait
PRB Lockscreen with Assert Dialog Causes Problems with Controls
PRB ACTIVATE WINDOW Command Not Working for Toolbars
PRB BINTOC Fails with SET COLLATE TO GENERAL
PRB VBScript Type Mismatch Error When Field Type Is adNumeric
PRB Header Corruption Causes END TRANSACTION to Discard Appended Records
PRB Page Size of Embedded Word File Changes When Edited
PRB Application Unable to Edit Database Files
PRB BorderStyle Property May Prevent Taskbar from Appearing
PRB Error in Remote View Wizard When Connecting to DB2
PRB FoxPro Corruption Problems Fixed by Novell 3.11 Patches
PRB VBScript Type Mismatch Error When Field Type Is adNumeric

Back To Table of Contents   Back To Top

Various MS Links

DLL Help Database Microsoft
Windows XP Home Edition Comparison Guide MS Windows XP
$49 Training (promotions-labs) labs Microsoft Training & Services
Microsoft Windows Update -
providing critical updates, security fixes, and software downloads
Microsoft
Internet Explorer 6 Public Preview Microsoft
Managing Duplicate Microsoft System Management Server Unique Identifiers MS TechNet
Scripting Clinic Scripting the Experience with Microsoft Office Developer MSDN Online Voices
HOWTO: Use Visual Basic to List Active Logical Drives Microsoft KnowledgeBase
HOWTO: Use Visual Basic or ASP to Create an XML Spreadsheet for Excel 2002 Microsoft KnowledgeBase
ACC2000 How to Programmatically Create, Search, Replace, and Modify Code Microsoft KnowledgeBase
OL2000: (CW) How to Create a Server Side Rule to Auto-Reply with a Specific Message Microsoft KnowledgeBase
WD97 How to Add a Portrait Page Number to a Landscape Page Microsoft KnowledgeBase
ACC97 How to Repair a Damaged Jet 3.5 Database Microsoft KnowledgeBase
Microsoft Train Simulator Microsoft
Tackle Common Tasks With These HowTo Resources MS TechNet
Microsoft Project Assessing and Managing Risks MS Office Support
Chart Workspace Object Model MSDN
Spreadsheet Object Model MSDN
International Features in Microsoft SQL Server 2000 MSDN
Unchecked Buffer in ISAPI Extension Could Enable Compromise of IIS 5.0 Server Microsoft Security Bulletin MS01-023
MSN Explorer 6.1 MSN
FP2000: Where to Obtain the Microsoft FrontPage 2000 Software Developer's Kit (SDK) MS TechNet
Free Tool Downloads - Windows 2000 Resource Kit software tools MS Windows 2000
Windows 95, updates, updating, downloads, service packs, communications, networking, power toys, kernal toys, preview, etc. MS Downloads: Windows 95

Back To Table of Contents   Back To Top

.NET

Visual Studio.NET Beta 2 Slated For June Techweb News
Microsoft scales back changes in VB.Net ZDNet eWEEK
Microsoft redrawing Visual Studio.Net Tech News - CNET.com
Why .NET will not become Microsoft's next OS-2 TechRepublic
Microsoft opens tech center to nurture .Net Computerworld News & Features Story
Seagate Software
Crystal Reports® for the Visual Studio.NET Platform
 
Visual Studio.NET Comes Up Short angrycoder.com
Microsoft Is All Talk About .NET Forbes.com
Tuning up for C# Tech News - CNET.com
Deciphering Microsoft's .Net puzzle Network World Fusion, 04-16-01
Serving the Web Windows Forms in Visual Basic.NET MSDN Magazine, April 2001

Back To Table of Contents   Back To Top

XML

Web consortium adopts specification for XML definitions Computerworld News & Features Story
Microsoft Releases SOAP Toolkit, XML Resources On MSDN Computer Reseller News
What's New in the April 2001 Microsoft XML Parser 4.0 Technology Preview MSDN
INFO List of Issues Fixed in Microsoft XML 3.0 Service Pack 1 Microsoft KnowledgeBase

Graphical XML editor gives developers added power

Intelligent Enterprise Magazine Product Review
Wall Street Releases Draft XML Standard Computerworld Resources-Research Story
Data Junction Refreshes XML Portion of Integration Suite Enterprise Magazine
Find a home for your XML data idg.net
A Brief History of SOAP XML.com  [Apr. 04, 2001]
Microsoft updates SOAP toolkit, further supports Web services standards InfoWorld
Soap Toolkit 2.0 RC0 MSDN Online Downloads
Industry Closer To XML-Based Web Services Standards, IBM Says Computer Reseller News
@court XML Lifts The Burden Of Paper Techweb News April 9, 2001
A Quick Introduction to XML Schemas iftech journal
XML: The Big Picture Network Computing Feature Business Applications
XML Special Report: Dishing Up Dynamic Content Network Computing Feature Business Applications
Web Consortium issues XHTML recommendation Computerworld Resources-Research Story
XML: The Big Picture Techweb News XML April 24, 2001
Intuition and Binary XML XML.com [Apr. 18, 2001]
Using OPENXML Microsoft
Retrieving and Writing XML Data MSDN
XML Schema confounds the experts ZDNet News
XML Beyond HTML Sun Microsystems
XML schema catches heat ZDNet eWEEK
XML Linking Language (XLink) Version 1.0 w3.org
Tutorials - XML messaging with SOAP developerWorks Components Education
New XML image standard under development InfoWorld
Extensible Markup Language: XML's Tower Of Babel InternetWeek In Depth
Gorilla Tactics Computerworld Resources-Research Story

Back To Table of Contents   Back To Top

General Interest

Windows XP to ship in October Tech News - CNET.com
It's Up To You To Pave The Way To The Information Society InternetWeek Opinion Rash's Judgment
How To Write Unmaintainable Code  
Enough With The Whining!
Have you noticed which company is acting like there is no downturn in the technology sector?
ZDNet Interactive Week
Microsoft Linux - the premier linux distro  
EU to turn US e-tailers into Euro tax gatherers The Register
Why Windows XP really was worth the wait ZDNet Story
1394 reasons to like Win XP ZDNet eWEEK
WinXP: why it's the Big One that could make MS bigger yet The Register
Windows XP, a.k.a. Whistler, impresses our reviewer Windows 2000 Advantage - Columns
Analysts and users angry over Windows XP vnunet.com
WinXP: product activation, updates and control freakery The Register
Windows XP Hits Home Wired News
M$ confirms WinXP won't support USB 2.0 The Register
Microsoft unplugs Intel project ZDNet News
Microsoft reveals XP flavours and prices vnunet.com
How will Windows XP affect Windows 9x TechRepublic
WinXP falls over old Cisco bug The Register
Update Cisco fixes switch-Windows XP problem that hit Xerox network ComputerWorld News & Feature Story
Microsoft partner criticises .Net vnunet.com
Microsoft trims hiring plans Tech News - CNET.com
Microsoft offers commerce tools to FrontPage users InfoWorld
Microsoft retools MSN Explorer Tech News - CNET.com
Advertising Humor Is at Center of Microsoft's New Campaign NY Times (Registration Required -- Free)
MS ridicules itself to sell Office XP The Register
Office XP test drive almost free Tech News - CNET.com
Microsoft, Windows 95, Windows NT, DOS
Microsoft Phasing Out Support for Old Software
Internet Week
Office 95 doesn't make the upgrade Tech News - CNET.com
Some managers are not worried about losing Win95 support TechRepublic
Microsoft, Windows 95, Windows NT, DOS--
Microsoft Phasing Out Support for Old Software
Internet Week
Where's NT 4 Service Pack 7? Enterprise Magazine
Microsoft Cancels Windows NT 4.0 SP7 Wininformant.com
Exploit devastates WinNT/2K security The Register
What's All the Fuss About?  -- At the heart of Windows 2000 is essentially a database - the Active Directory. ENT Article Archive
Microsoft alters Passport Terms to stem Hotmail defections The Register
Microsoft alters Passport terms of use InfoWorld
MS Revises Passport Terms Wired News
Developer of Notes apps embraces Exchange ZDNet eWEEK
IE6 beta bug can blank out email The Register
Microsoft reshuffles executives, creates separate .Net group Computerworld News & Features Story
Specialist IE, Windows can mask dangerous files InfoWorld
HTML bug hits Internet Explorer vnunet.com
Millions of IM users go dark ZDNet News
AOL Instant Messaging service out again InfoWorld
VC veteran named to White House tech council ZDNet eWEEK
Microsoft a year later Has anything changed ZDNet News
Internet Critic Takes on Microsoft NY Times (Registration Required -- Free)
Windows tech: 10 steps to creating an active directory Computerworld Resources-Research Story
More programmers going Extreme Tech News - CNET.com
Programmers adopt 'Extreme' methods ZDNet News
White Paper: How to get Custom Software to market in 8 weeks or less: Applying the principles of Extreme Programming for business success Geneer
Programming gets extreme Computerworld News & Features Story
MIT Short Circuits Distance-Ed Sellers Forbes.com
Lack of standards blocks supply-chain automation Computerworld Resources-Research Story
My life as a cable and DSL guinea pig--and what I learned ZDNet Story
Why you should be using FireWire ZDNet News
When Is Software Ready Ship it Anyway! MSDN
Volvo Delves Into Web-based Car Design Computerworld Resources-Research Story
Philippe Kahn is never at a loss for a technology idea The Net Economy
Despite layoffs, the labor market it still tight The Net Economy
Tuning up Windows 98: Nine tips that are often overlooked TechRepublic
KBINF OLEDB Connect Info from ODBC AccessHelp.net
KB009: Shape Captions Made Easy  
Stop the OS insanity ZDNet News
Linux founder trashes Mac OS X foundation Tech News - CNET.com
MacOS X is 'crap' - Torvalds The Register
Apple & Microsoft Déja vu all over again ZDNet News
Core OS differences from Apple ZDNet eWEEK
Don't blame me, I'm only the operating system Windows 2000 Advantage - Columns
Microsoft licenses speech-recognition technology InfoWorld
Perfecting Your Professional Pitch MSN Careers
There's No Foolin' In E-Commerce Transactions Computerworld Communities Story
Police Policed With Data Mining Engines Computerworld News & Features Story
Involve the user-- A better way to develop TechRepublic
Automated Clearing House Computerworld Resources-Research Story
Find Registry Facts Fast ZDNet -- PC Magazine Solutions
An e-Tailer's Guide to Credit Card Processing DevGuru Tutorials
Back to school with a vengeance
You need to brush up on security. Hacker classes are now in session. Should you attend?
ZDNet Sm@rt Partner
Windows Insider NAT Expands the Net MCP Magazine
All You Need Is Bandwidth Computerworld Resources-Research Story
Bandwidth Constraints Begin to Worry Schools NY Times (Registration Required -- Free)
Protect your always-on Internet connection from intruders ZDNet Story
INTERNET CONNECTION SHARING
A three-part series on sharing your broadband connection.
Part 1: Overview
Part 2: Software
Part 3: Hardware
TechTV
ADSL Tutorial The Net Economy
Broadband Dreams Deferred Network Computing Top of the Stack Network & Systems Infrastructure
DSL on the rise Techweb News April 6, 2001
Digital Subscriber Line (xDSL) FAQ The Net Economy
A cell phone that beats DSL Why this changes everything ZDNet Story
The Downside Of DSL, Cable Modems Information Week
DSL Sharing TechTV
Get the Most Out of Your Broadband Connection TechTV
Use Proxy Software to Share a Broadband Connection TechTV
WinProxy for Home Users - Home Page Ositis Software
WinGate - Internet Sharing-Proxy Server Solution Deerfield
Why Wireless Needs a Hard Look Computerworld News & Features Story
802.11 and swiss cheese ZDNet News
Wireless Short Message Service (SMS) Tutorial The Net Economy
Integrating wireless technology digitalMASS at Boston.com
Using ASP to Send a Wireless Text Message 15 Seconds
War driving by the Bay The Register
Can Ethernet be stopped? ZDNet Sm@rt Partner
Five NetMeeting Secrets TechTV
UML for the Whole Lifecycle
The second half of a look at Rational Rose 2001 and the AnalystStudio suite
Intelligent Enterprise magazine
How To Harness Distributed Object Architectures (Page 1) Network Computing Workshop Network & Systems Infrastructure
Adobe Acrobat Reader 5 - Download Adobe
Net Hail for Windows NT - GUI interface to NET SEND command  
RealPopup homepage -- Freeware WinPopup Replacement  
BootDisks - CDrom Files -Quality Utilities And Tools BootDisk.com
Color Cop ZDNet - PC Magazine PC Labs
Startup Control Panel Mike Lin's Home Page
ADO Explorer 1.51-- adox151d.exe
Is a powerful database tool using ADO (ActiveX Database Objects) that allows you to proceed quickly and efficiently in your everyday database work. Now comes with ready to use native SQL commands for MSSQL, ORACLE and INFORMIX servers!
Programmers Heaven - Where programmers go!
Serial Communication library for Win32 (event-driven or not) V1.0 - serial.zip
The package contains the source of two files describing how to
interface the serial port for Win32 applications. The modules presented can work either in event-driven application (GUI,...) or in non-event-applications (like console application).
Programmers Heaven - Where programmers go!
Directory Report -- wdir.zip
Is the file manager for Windows 9x/NT/ME/2000. Easy to use and packed full of features, Directory Report has something to offer for everyone.
Programmers Heaven - Where programmers go!
IE_NAV_REF.zip -- A list of key objects, properties and methods and their
Availability across MSIE and Netscape Navigator
Programmers Heaven - Where programmers go!
MS Agent Info (Zip file)  
Symantec Unleashes 7.0 Version of Ghost Enterprise Magazine
AppendPDF -- Create One PDF From Many Appligent
Connectix previews virtual Windows, Linux even OS/2 tech. The Register
New version of Zone's firewall on the way ZDNet eWEEK
Registry Healer KsL Software
Products - Annotation OCX SDK Black Ice Software Inc.
Wintellect - Free Tools Wintellect
Lucent NetworkCare - MyVitalAgent (Free Tool Download) Lucent Services
Fill that toolbox before tackling Active Directory Network World Fusion
Web Tip: Add Functions to IE 5 TechTV
Shell Scripting 101, Lesson 1 Windows Scripting Solutions Online
Using the Counters Object 4GuysFromRolla.com
Connections And Server Database Permissions PowerASP.com
COM Caffeine for your ASP
Lesson 1 :  Introduction to ASP/COM
FindTutorials.Com
Transfer Video to CD TechTV
Moving Molecules With Nanotechnology TechTV
Paperback Music One Solution to the MP3 Debate N.Y. Times (free registration required once)
Download our RJ-45 module installation guide TechRepublic
Plextor PlexWriter 16-10-40A TechTV
Disable Onboard Video Card TechTV
Dual Monitors TechTV
Get Rid of Your Windows 98 CD TechTV
Sharing an Internet Connection TechTV
Visualizing Change
"solid modeling" CAD/CAM software
InternetWeek Transformation Today
MS SQL Server: Dynamically manipulating files using xp_cmdshell searchDatabase.com
The Database Specific Search Engine
What you don't know about denormalization can hurt you, Part I searchDatabase.com
Database Tips - Key database design concepts searchDatabase.com
Best Database Web Links - ADO, OLEDB searchDatabase.com
Working with Databases and International Date Formats 4GuysFromRolla.com
Tracking SQL Server crises: Configuring automatic alerts TechRepublic
SQL Server: Inserting Images ( binary data ) into Database stardeveloper.com
SQL Server: Tame Those Strings Part 5 - Using STUFF SWYNK.COM
MSDE - Desktop Engine SWYNK.COM
Queries On Multiple Databases SWYNK.COM
SQL7 Replacement for Access Crosstab
RAC, the R(eplacement) for the A(ccess) C(rosstab) query, written exclusively for MS Server 7 and S2k
 
Oracle PL/SQL -- The Complete Video Course: 1/e Welcome to the DigitalGuru Computer Bookshops
Access Reports on the Web - Stupid VB Tricks accesshelp.net
Programmatically changing a user's Windows password with Visual Basic TechRepublic
Win2K Task Scheduler Windows 2000 Magazine
Registry Secrets Windows 2000 Magazine
10 steps to creating an active directory ITworld.com - Windows tech
Get Rid of Your Windows 98 CD TechTV
Chase Away WinMe Annoyances TechTV
In space, no-one can fix Windows NT vnunet.com
So You Want to Be a Rocket Man MSN Careers
Understanding DCOM - Part I iftech journal
Understanding DCOM - Part II iftech journal
Understanding DCOM - Part III iftech journal
PostScript Programming DevCentral Learning Center
EC Tips: Mail and Purge internet.com's Electronic Commerce Guide
What Is Internet Addiction? TechTV
Dramatic increase in virus attacks predicted ZDNet Story
Field of screams
You wouldn't know it from the shouting match, but Windows 2000 and Linux can play side-by-side in the same league. Here's how.
ZDNet Sm@rt Partner
Preparing for Linux in your enterprise TechRepublic
Linux worm attempts to take over insecure servers The Register
Adore worm squirms in Linux systems Tech News - CNET.com
New worm targets unprotected Linux systems Computerworld Resources-Research Story
Virus Threat to Linux Low Internet Week Security Experts April 6, 2001
NSA funds work to thicken Linux armor Tech News - CNET.com
You can't always get what you font (Linux) ZDNet News
Sony releases Linux for Playstation2 The Register
Peace, love and...Linux ZDNet News
Apache 2.0 beta hits the Web Computerworld News & Features Story
Holes found in file server software Tech News - CNET.com
Flaw found in common Internet standard Tech News - CNET.com
FTP software open to attack ZDNet News
FTP software flaw could allow remote attacks on servers Computerworld News & Features Story
Transferring Files Be Careful Wired News
Big Blue hacker for hire ZDNet Sm@rt Partner
3Com Beware The Inner Enemy ZDNet Interactive Week
Demand for IT workers is down 44%, study says (4-01-2001) SiliconValley.com
Tech recession to bottom-out in a year - Marimba CEO The Register
Live mouse found inside PC The Register
Work at light speed--try some simple time-saving tips ZDNet Story
Slot Car Tracks Meet The Computer Age Speedvision Online
Adobe moves toward network publishing InfoWorld
AGP Versions TechTV
Finding Time to Think Computerworld Resources-Research Story
Keeping Workers Revved Up During Tough Financial Times Computerworld Resources Story
ADO.NET and Access 2002 Smart Access May 2001 - Editorial Changes
Tech ageism works both ways Tech News - CNET.com
Experts: computers slouching towards usability Computerworld News & Features Story
Apple - Trailers - The Dish Apple
Taxing Overhaul
IRS standardizing data, access in $10 billion gamble
InternetWeek Apr. 19, 2001
Biggest waste of worker's time--e-mail ZDNet News
Fly brain software takes to the sky The Register
'Tractor beam' technology advances BBC News SCI-TECH
Making HAL Your Pal Wired
New Chip Prevents Car Thefts MbizCentral, Gateway to the Mobile Economy
Spycam Small Enough for Mobile Devices MbizCentral, Gateway to the Mobile Economy
Let's get visual! Why you absolutely need to get a Webcam ZDNet Story
Guest Opinion: Cross the Other Digital Divide Visual Basic Programmer's Journal

Back To Table of Contents   Back To Top

TIPS 'n TRICKS

By the Membership

GenLoader - A WSH Loader Wizard

Application GenLoader.SCX
Purpose The purpose of GenLoader is to allow you to easily create WSH scripts to update an application executable by using one of two means to check currency.
Requires   Microsoft Visual FoxPro 6.0 or better
  GenObjs class library (included)
  Open bitmap (included)
Authors John Koziol, MCSD, MVP
  (850) 893-2302
  digitalelite@earthlink.net
  George Tasker, MVP
  GTasker@compuserve.com
Version 1.00 (April 21, 2001)
Copyrights   GenLoader ã2001, John Koziol
  WSH Loader FAQ, ã2001, George Tasker

Download 'GenLoader.zip' here Download GenLoader.zip

Back To Table of Contents Tips   Back To Top


Benchmarking: Microsoft Visual FoxPro 6.0 vs. Inprise Delphi 5

I know that all of you would be interested in this kind of benchmark. I was
too until I got the chance to do it. As a student, you get the chance to meet
people and PEOPLE. I started to work in database programming since I was a
child (5th grade) and I was always interested in benchmarking software
like you too (I think).

I made 4 tests:

I used 2 identical computers (P II 233, 128 MB) and a programmer in Delphi
offcourse (I don't know a thing in Delphi).

We used two tables with 1,000,000 records. In the Delphi (ISAM) table we
used a float field NOT autoincremented-- in the FoxPro one I used a 7 numeric
field long.

We then made some code that will increment the two fields and started the test. We
made the adding code in two ways - append and Insert.

In the append method, FoxPro was adding about 2291 records per second and
Delphi about the same (2154).

In the insert method FoxPro was adding about 8851 records per second and
Delphi about (4456).

So as you see FoxPro is better at doing Inserts than Delphi.

Then we tried the same test but we added a MySQL server.

We couldn't measure the time because my neighbors (also students on the
campus) were playing games on the network and the results where random.
Once Fox was faster and after one minute FoxPro worked like a snail. The same
with Delphi.

In the end of the test, we drank some beer and got drunk.

[Ed. Note:  Please don't drink and test.  Do your testing first.]

Well, Delphi is superior in multimedia and stuff like that.  But, if you need
speed, you will have it only with FoxPro.

Ionut D. Baldazar

Phone: +4093026686
E-mail:
ionutb@c3.campus.usv.ro

Back To Table of Contents Tips  
Back To Top


Visual Class Navigators Library

The library of visual classes Navigators contains three simple classes - CKhFullNavigator, CKhMidiNavigator and CkhMiniNavigator. These classes are developed for creation of typical managing and navigating sets of buttons for forms.

Download 'Navigators.zip' here Download Navigators.zip

Alexander Khudyakov
mailto:akhudyakov@yahoo.com

Back To Table of Contents Tips   Back To Top


How Can I Color Code Records/Cells In A Grid?

If you want to add colour coding to a grid to be able to visually differentiate between records and/or cells, add 2 new fields (fore_color C(11), back_color C(11)) to the ControlSource table of Grid1.

Add a new form method called .GridColours() and a new form property called .nRecNo.

In the .Init event of Grid1 put:-

THISFORM.nRecNo = RECN([TABLENAME])
THIS.Refresh()

In the .AfterRowColChange event of Grid1 put:-

THISFORM.nRecNo = RECN([TABLENAME])
THIS.Refresh()

In the .Column(s) of the grid set .Sparse = .F. and .DynamicCurrentControl = THISFORM.GridColours().

In the .GridColours() method put:-

WITH THISFORM.Grid1
    DO CASE
    CASE THISFORM.nRecNo = RECN([TABLENAME])
        .Column1.Text1.Forecolor = RGB(255,255,255)
        .Column1.Text1.Backcolor = RGB(0,0,128)
    CASE TABLENAME.fore_color = [R/W]
        .Column1.Text1.Forecolor = RGB(255,0,0)
        .Column1.Text1.Backcolor = RGB(255,255,255)
    CASE TABLENAME.fore_color = [B/W]
        .Column1.Text1.Forecolor = RGB(0,255,0)
        .Column1.Text1.Backcolor = RGB(255,255,255)
    CASE TABLENAME.fore_color = [G/W]
        .Column1.Text1.Forecolor = RGB(0,0,255)
        .Column1.Text1.Backcolor = RGB(255,255,255)
    ENDC
ENDW

You will find the .Forecolor properties of the cells will reflect the value selected by TABLENAME.fore_color, with the currently selected record highlighted.

If you want the fields TABLENAME.fore_color and TABLENAME.back_color to determine the colours displayed, then :-

    DO CASE
    CASE ALLT(TABLENAME.fore_color) = [255,255,0]
        lcColor = [.Column1.Text1.Forecolor = RGB(]+ALLT(TABLENAME.fore_color)+[)]
        &lcColor
        lcColor = [.Column1.Text1.Backcolor = RGB(]+ALLT(TABLENAME.back_color)+[)]
        &lcColor
    ENDC

Chris R. Chamberlain
support@lithoplas.com

Back To Table of Contents Tips   Back To Top


Set Your Exe's Start Path On The Fly

Forget about the problems caused by shortcuts badly created. With this line
of code your exe will always start in the directory from where it was
called. I think that this line must be the headed one of any program.

If Version(2) = 0 THEN
Cd (LEFT(SYS(16),AT('\',SYS(16),OCCURS('\',SYS(16)))-1))
ELSE
*-- may be here you want to set your development environment...
ENDIF

Ponga su directorio de inicio facilmente

Olvidese de los problemas causados por accesos directos mal creados.
Con esta linea de codigo tu exe siempre se parará en el directorio desde
donde se llamó.
Yo pienso que esta linea debe ser el encabezado de cualquier programa.

If Version(2) = 0 THEN
Cd (LEFT(SYS(16),AT('\',SYS(16),OCCURS('\',SYS(16)))-1))
ELSE
*-- Aqui tal vez quiera setear su entorno de desarrollo...
ENDIF

Ariel Gimenez
arielgimenez@yahoo.com
Argentina

Back To Table of Contents Tips   Back To Top


VFPLife.zip - A VFP6 Mathematical Game

Application Life.SCX (the Life Form)
Introduction

Life was invented by the mathematician John Conway in 1970. Conway wanted to create a simple set of rules that defined the evolution of patterns. He was successful; Life starts with very simple patterns and only a handful of rules and can generate complex, beautiful, and sometimes unpredictable patterns.

The rules are easy:  A cell that is turned on is alive; others are not. Each cycle, all surrounding cells are evaluated. If a cell had one or no neighbors, it dies. If it has three living neighbors, it stays alive. If it has 4 or more living neighbors, it dies. An empty cell becomes alive (is born) if 3 neighbors are living.

These rules are evaluated for the matrix as a whole and then plotted.  And reevaluated, replotted…and so on.

To quote astrophysicist Mario Livio from his book, The Accelerating Universe, “The moral from this game is simple: even with a very simple set of fully deterministic laws, an evolving system can achieve a high level of unpredictablility and complexity.”

Numerous computer programs have been written to play Life. Life was one of the very first programs I ever wrote on an Apple ][ in the late 1970s.

Requires  Microsoft Visual FoxPro 6.0 or better
  Lifeforms class library (included)
Author    John Koziol, MCSD, MVP
  (850) 893-2302
  digitalelite@earthlink.net
Version       1.00 (April 14, 2001)
Copyrights ã2001, digital elite services and John Koziol

Download 'VFPLife.zip' here Download VFPLife.zip

John Koziol, MCSD, MVP
digitalelite@earthlink.net

Back To Table of Contents Tips   Back To Top


Copying Files?

copy.gif (4382 bytes)

Source: http://www.kicken.com/images/copy.gif

Back To Table of Contents Tips   Back To Top


Joke of the Month

Comments area

I've been a member of VFUG now since October of 1997. I really like this place with all of the many different countries of the world represented here. It never ceases to amaze me how many different ideas pass through this website and people are always willing to share what they know with others. When I was asked about Microsoft's marketing of Visual FoxPro over the last few years, I quickly replied, "I can't believe it's not better!"  Long live Visual FoxPro!

Fabio
International Celebrity
Residence: Can't Tell You

Back To Table of Contents Tips   Back To Top

ON SHUTDOWN....

Some final comments....

Generally, regular CHAT sessions are held every Thursday.  For more information on scheduled chat sessions here at VFUG, contact John@VFUG.Org or check the VFUG newsgroup out for current info.

Want to be (in)famous? All you have to do is send VFUG an article or otherwise useful piece of content. We'd be happy to publish most Fox related tidbits, and you can help your fellow developers worldwide!

VFUG is also always looking for additions to our Vendor program. Would your company be interested? Contact Carl@VFUG.Org to get with it.  Also, if you are interested in your company placing a banner advertisement on our home page, please contact Carl.

Back To Table of Contents   Back To Top

Virtual FoxPro User Group
Copyright © 2001, Virtual FoxPro User Group, Orlando (Florida), All Rights Reserved