Session E-DIST

Distributing VFP Applications

Eldor Gemst
ELGEM Associates


Overview

Once you've built and tested your VFP application, the next challenge is how best to package and distribute it. This session guides you through taming the VFP Setup Wizard and making a professional installation. Also covered are strategies for deploying over a network, handling different drives and directories, where components should be installed, automating setup issues, making sure your workstations always have the latest version of your software and issues involved with extras such as ActiveX controls. This session also discusses other software that can be used to produce distribution disks. If you distribute or plan to distribute your applications, this session is a must for you.

One of the more embarrassing experiences a developer can have is delivering an application, having all the client staff gather around to see the wonderful new program, and… Crash! The application doesn't run. The chance of this happening to you is directly proportionate to the number of people gathered around. And of course if it's the person who signs the check for your invoice who's watching, you are just about guaranteed that your application won't run after you "install" it. Ok folks, I'll admit it right at the start… unfortunately I speak from experience. It's happened to me, and because of that I've learned (sometimes the hard way) how to distribute applications properly. And I'm going to share some tips with you so that you can avoid that terrible fate.

Check Your App

This point I cannot stress strongly enough. Run your application before trying to distribute it. By this I mean that once you've built your application and compiled it to an EXE, you need to check it by using Explorer, (not by using VFP from the command window) to find it and then double click on it. Watch particularly for screen "flash" and general appearance. Does it look professional as it starts? (Heck, does it even run?)

The first most common problem developers new to application distribution often run into is that when they try to run their newly-installed application at the client site, it seems that the application comes up for a moment and instantly disappears. Running the EXE from Windows Explorer should let you catch (and fix) this before you deliver to your client. The reason this happens is that when you run something (like a form) from the command window on your development machine, there is a "wait state" that keeps the application "up"… keeps it running. That wait state is the Command Window itself. If the application really was running, you wouldn't see the Command Window.

Setting up a wait state for your application prior to Visual FoxPro was more difficult than it is now, involving something called the dreaded "Foundation Read". Today this involves using only two lines of code: A "READ EVENTS" to hold your application running, and a "CLEAR EVENTS" to close it when done. It's most important to hook up your CLEAR EVENTS (to a Quit option on your menu perhaps) before you issue a READ EVENTS, because otherwise it's reboot time. Not nice.

Build (to an .EXE) the EX1 project and run it from the Command Window. You'll see it appears to work correctly. Now exit VFP and find the EX1.EXE file with Windows Explorer and run it by double-clicking it. This time you'll see a flash and then the app disappears. Not quite what you expected, right?

Take a look at the EX2 project. The difference is that we now have READ EVENTS and CLEAR EVENTS code hooked up. You'll find the READ EVENTS in the MAIN.PRG right after launching the menu, and in the menu in a Quit option, you'll find the CLEAR EVENTS. Build EX2 and try again. This time you'll see the application stays running until you click Quit from the menu. On to the next most common problem…

If you were paying attention when you ran the last EX2 example, what you probably saw is that when your application started it was not full-screen. That didn't look too cool, did it? After a little digging, you find that if you put _SCREEN.WindowState = 2 in the beginning of your main program you will maximize your application screen. Build the EXE and try this again through Windows Explorer. Now the application will be full-screen, but there's another problem… There's the most annoying screen flash as your application starts up less-than-full-screen and then maximizes. Not slick and professional - by any means. Luckily this is fairly easy to fix.

The solution involves two things: One, you need a CONFIG.FPW file with the following lines: 

     
    SCREEN=OFF
    _SCREEN.WindowState = 2

Second, you need _SCREEN.Visible = .T. in the beginning of the main module of your application. That's all there is to it!

There are more serious things to check than the screen size. Having your application run from the development environment (command window) really is no guarantee that it will run under the runtime version of FoxPro on your client's machine. The data path (including drive and directory) may be different on your development machine than the client machine. As a serious developer, I actually have a separate machine which I use for testing installations and that machine doesn't have VFP installed on it. Whatever method I use for producing my distribution files (these methods will be discussed later in the session), I actually test the full installation process before going to the client.

Location, Location, Location

Something that often causes problems is the path to the data. I'm a firm believer in not reinventing the wheel, so I use a commercial framework (several actually) for my applications and the good ones use varying techniques for handling differing paths. One idea is that in each form's BEFOREOPENTABLES event in the Data Environment, you can check a property of your application object (or a variable) to see what the current database for your application is set to, and then set it the same in the form. Something along these lines: 

 
      THIS.Cursor1.database=oApp.cDBC

The above requires that the cDBC property is a fully-qualified DBC name including the path and .DBC extension. "What if you have form classes?" you ask. The problem of course is that there is no data environment in a form class - it is only available in the instance of the class. How do you get around that without having to duplicate lots of code in every form instance to do this? There are several approaches, but the one I like best is to create a custom method in the form class, maybe called SetDatabase, to do this, and then simply add a call to that method in the form's BEFOREOPENTABLES method in the data environment of the form instance. (That's much easier and cleaner than having to repeat a whole bunch of code, every time, in the form instance. You'd only be repeating a single easy call, like THISFORM.SetDatabase().

Here's an example of the SetDatabase method. I'm trying to keep it simple here to illustrate the concept rather than full details of the implementation, and a likely enhancement would be to make SetDatabase figure out how many tables are in the data environment of the form instance and set the database properties for each one. My example only sets it for a single table, using the Cursor1 object of the data environment. 

 
      THIS.DataEnvironment.Cursor1.Database = oApp.cDBC

I should point out that if you try to test the above in a simple form, it won't work. You need to actually create an instance of your form class (with the above code in the custom SetDatabase method of your form class) and then test it. The reason it won't work is that when the BeforeOpenTables method is run and it calls THISFORM.SetDatabase(), your form doesn't really exist yet. Remember that the last thing to run when the form instantiates is the form's Init() event and only when that happens, does the form exist. So we need to get to this code via inheritance and then it does indeed work. Pretty sneaky, huh?

Some framework authors prefer to create data environments manually via code instead of using the real DataEnvironment of a form and then programmatically manipulate the required properties. This works equally well, and your decision about which way to proceed really should be based on other factors than simply how you can set data paths for your application.

Printing Issues

One of the surprises with VFP reports is that they get hard-coded with the name of the printer used at the time of creation. I've found that for best results, before packaging up an application (APP or EXE) that clearing that printer name is the thing to do. I have all the reports for the application together in one subdirectory and it's very easy for me to run this short program: 

      reset.prg to reset all printer-specific settings to blank
      LOCAL lnx
      LOCAL ARRAY laReports[1]
      =ADIR(laReports, “*.FRX”)
      FOR lnx = 1 TO ALEN(laReports,1)
         WAIT WINDOW “Processing File: ” + laReports[lnx,1] NOWAIT
         USE (laReports[lnx,1])
         REPLACE TAG WITH “”, TAG2 WITH “” NEXT 1
      NEXT
      USE
      WAIT WINDOW “All done!” TIMEOUT 1

Custom Icons

A nice finishing touch for your application is the addition of your own custom icon. This is what will be used for your application in the Windows Explorer listing in Windows and also in the Task Manager when you switch back and forth between running applications. It's not hard to do this, but here are some tips to help you.

An icon "resource" file used for your application must actually contain two versions of your icon. One is used for Windows Explorer (small icon) listings and should be 16 x 16 pixels and the other is used for Task Manager and Explorer when large icons are used. The second one should be 32 x 32 pixels.

So assuming you have artistic talent, how do you create such an icon? If you're lucky enough to still have VFP 3.0 or 5.0 available, there was an icon editing program included. Otherwise there are several good shareware programs available on the Internet. I've had good success with one called MicroAngelo.

APP or EXE?

In days gone by (talking about FoxPro for DOS here) there was a valid reason for making an EXE file instead of an APP file. That's because there were two flavors of EXE file available; Compact and Stand-Alone. The Compact file was just about the same as an APP file - either required the FoxPro runtime files to work. But the stand-alone version was exactly what the name implied, a stand-alone file that would run without requiring any external runtime support files. In practice there wasn't really much difference between compact and stand-alone EXE's but developers often chose the stand-alone EXE (I think because it made them feel more like "programmers"). But we're not using DOS anymore (you're not in Kansas anymore) and Windows changes things. There's no longer a stand-alone EXE available to us. (Nor is there in VB so we are not a second-class citizen here.)

Now our choice is APP or EXE and either one requires the same support files to run.

There is however a new reason for us to consider using EXE files. Here's what I do: For all my in-house testing and early beta testing I usually make an APP file. When I'm ready to give a copy to outside testers (or my client) I switch to making EXE files. This is because there is a new option available when you choose EXE called "Version". Here's what the dialog looks like:

Version information can easily be added to the EXE file.

The idea here is that you can have automatic built-in version numbering which makes it quite easy to tell what version a client is using if they call with a support question. Just make an About… form with the current version displayed. (We have a new AGETFILEVERSION() function to get information about our EXE file.)

ActiveX

Distributing ActiveX components poses additional challenges. Not only do we need to make sure the control is present and registered on the client machine, we need to make sure it is the correct version. Even if you use one of the ActiveX controls that comes with Visual FoxPro, if you upgrade your system and the client does not, often your control will no longer work. The problem is that ActiveX controls are not backward compatible; only forward compatible. Installing various software often gives you newer versions of the OCX files, causing things to break.

For example, if you use the TreeView control (which comes with VFP and is included within the COMCTL32.OCX file) if you get a newer version of that OCX, even though your client also has the TreeView control, it simply will not work unless they also have the same version. Your client will get an "OLE class not registered" error. And you cannot simply install the correct version on the client machine as that would likely cause other software to break.

The solution to this (and other) problems with ActiveX controls is simply to instantiate them at runtime on the client machine instead of at development time. That way as long as the control exists (and you can install it if it doesn't) the version instantiated will always be the one the client has.

Since the purpose of this session is to point out the challenges and solutions to application distribution and we have a lot to cover, we cannot go into this technique in full detail here. But I do want to point you in the direction of Doug Hennig, who wrote a full article on this problem which appeared in the June 1998 issue of FoxTalk Magazine. Doug has also written a tool to help you load (instantiate) your ActiveX controls at runtime.

My own personal philosophy here is to avoid ActiveX controls whenever possible in a Vertical Market application because in such a situation you have much less control of the environment than you do with custom software. And if you cannot avoid using ActiveX controls, then you need to go the distance and implement a management solution which covers installation, registration and different versions of those controls. Doug's article and tool is a wonderful head-start on this.

Handling Updates to Programs and Data Structures

When we talk application development and distribution during this session, we really are considering a larger-scale system than just installing on a single non-networked computer. In our world today, networks really are the rule rather than the exception. A normal installation would have the data located on a server computer somewhere with it being accessed from numerous workstations. For the best performance, each workstation would have its own copy of the VFP runtime files and the application itself. (An application can be installed solely on the server and then the workstations can run it directly from the server, but considerably better performance can be achieved by putting the app on each workstation.)

During the life of a program there may be many changes that take place, from minor cosmetic changes to bug fixes to major enhancements. This would obviously require that all workstations receive updated versions of the application software when they become available. That often means someone must visit every workstation and install the newer version. Grunt work! A good programmer should never be doing the same routine stuff over and over again.

How could we automate the updating of workstations? Well, it's rather simple really. Let me explain a technique that works, and then you can elaborate on it to suit your needs. Instead of having just one application on the workstation, we set up two applications. The first app calls the second, and the trick is that before the first app calls the second, it checks to see if there is a newer version of the second application available, and if there is, it copies it to the workstation before running it. Sound simple? It is! For simplicity sake we will call our first program "StartApp" and our main application program "Main". Here is the pseudo-code: 

      * startapp program
      If there is a newer version
         Copy it to workstation
      Endif
      Run the main program

There are a couple of issues to address with this technique… First of all, the apps can be compiled as .APP files or as .EXE files, or a combination. It doesn't really matter. What does matter is getting the configuration correct for the installation. Many developers either use a CONFIG.FPW file in the startup directory to make required settings or they build that file into their application. Obviously if you are using a launching program such as I propose, it is that program (StartApp) which starts first and which must either have the CONFIG.FPW file built into it, or that file (if you're even using one) needs to be in the startup directory.

The other issue is how to tell whether or not there a newer version of the software available. If you are sure of the paths involved (meaning you know how all the workstations have the network drives mapped) you can code this information right into StartApp. Otherwise a good technique is to use an .INI file for your application which is read by StartApp to pick up the data and application paths. Then StartApp checks the directory on the server where updates might be located, compares the time/date stamp with the local version and if the version found is newer, copies it down to the workstation before running it. You could even have another text file on the server, containing a list of files that need to be checked and if you write your StartApp routine with this in mind, it can dynamically determine which components should be checked for newer versions.

Producing Distribution Files

Now that you've got everything ready (and thoroughly tested) you need to produce your distribution files. A factor in your choice of what product to use to create the distribution files will be what kind of application you have and who your client is. If you are selling to one client and you will be doing the installation, things can be pretty simple. But if you have a vertical market application (lucky you!) you'll want something slick and professional looking. For the least cost (and least sophisticated result) you can use the Setup Wizard which is included with Visual FoxPro. It's really pretty good and it works, but for a vertical market application you'll want one of the commercial products such as InstallShield Express by InstallShield Corporation or Wise InstallMaster.

Setup Wizard

In my opinion the biggest drawback of the included Setup Wizard is that it just doesn't look as slick as an off-the-shelf package's installation routine. But it is included with VFP and it does work, so for most developers it is the package they will be using. Here are a few tips to help you produce a successful installation…

The mistake many developers make when running the Setup Wizard occurs right at "Step 1 - Locate Files" in the Wizard:

This first step is where beginners most often go wrong

Unless you want to give your client all the source code files, you should not point to the application root directory. Doing so will produce distribution files of everything in that directory and sub-directories. Don't let this catch you by surprise. What you should do is create a new directory just for what you want to give your client (I usually call this \SHIP as shown in the screen shot) and then copy just the files I want to distribute to that directory. Don't forget to include a CONFIG.FPW file if you need one (this can actually be included - built into - your EXE file if you want).

The next step allows you to specify which major components (other than your application) you want/need to distribute. For example, it is normal to include the VFP Runtime support files. But the client only needs one set of those files, so if you've already delivered them one time, you don't need to do so again, unless you change (upgrade, patch) your VFP development environment. Likewise, even with more than one (different) VFP application, your client needs only one set of the runtime support files. The "interim release" which I'm using at the moment has several new options, which I will not describe here, so I will concentrate on distributing an application compiled to a regular .EXE file.

 

verherige Vortrag

Übersicht

nächster Vortrag