Developing a Database Application:
The dB2K Tutorial

Ken Mayer, Senior SQA Engineer, dBASE Inc.
Michael Nuwer, dB2K user
Last Modified: October 13, 2001
Version 2.2.0

Phase VIII
Creating the Startup Program

Goals and Objectives

The goals and objectives of this phase of the tutorial project are:

The Application as an Object

An interesting concept that we will adopt in our sample application is the idea of creating an object specifically for controlling the application.

The advantage to this is that we can create our own methods (such as "startup" and "shutdown") to handle specific code for the application.

It also means we are encapsulating the whole application within this object (which is proper for OOP). We will be taking this a step further by creating a generic application object and then subclassing it for this particular application.

Note: "Subclassing" is the technique of using a class as a "base" class -- creating a new class that inherits all of the properties, methods, and events of the base class.

An example of this is creating a form in dB2K (never mind "custom" forms) -- there is a "base" form class that is built in to dB2K. When you create a new form, it is a subclass of the base form. It has all the properties, events and methods of the standard form in dB2K.

Once you have subclassed an object you can modify its properties, events and even hook your own code into the methods of that class, without modifying the base class's definition.

To do this will mean writing a START program to handle creating the application object. The program will be used to actually set up whatever procedure files (and custom control files, etc.) are necessary for the application, create an instance of the application object, and execute the startup code.

Create the Start Program

To create the program go to the Navigator, click on the "Programs" tab, and double-click the "Untitled" icon. This will bring up the source editor with nothing in it.

You should probably place some comments at the beginning, explaining what the program is. When placing comments in the code, you should either use the comment "block" comments (as shown below) which start with "/*" and end with "*/", or place two slashes at the beginning of each line ("//"). If you are used to using the older asterisk (*) and double ampersand (&&) style comments, these work fine as well. Comments can be useful for you or anyone else examining your source code:

       Author: Ken Mayer
       Date..: November 8, 1999

       Tutorial project START program

       This program is used to start the TUTORIAL 

       There is a reference to another .CC, which is
       the genericMDIApp.CC file -- this contains
       the generic application object, and below we 
       subclass it ...

       The idea is that you have a lot of "things" that
       you always do for any application that is
       an MDI app. You create one object that handles
       all of that, and then you subclass that for
       the specifics for an application, as each is
       at least slightly different.

I am not going to spend a lot of time discussing such things as variable scoping (using local versus public, and so on). If you need to know more about these, they are discussed in the online help that ships with dB2K, as well as the Language Reference and the Developer's Guide.

The following code is the very beginning of the application. It can be used "as is":

   local app
   set talk off
   set procedure to additive

   app = new TutorialApp()

   return ( )

What the statement "app = new TutorialApp()" does is to create an instance of the tutorial application object. We will define this a bit at a time in the next steps. The "return" statement simply calls the method, which is code we will create -- this will be a method of the application object.

The Application Specific Object
In the same program file, you want to add the following (after the RETURN statement), which is the actual application specific part of the code:

class TutorialApp of GenericMDIApp
   // set any specific code for the subclassed
   // MDI application here:

   this.FrameWinText = "dB2K Tutorial Project"

   // note this is not the file name -- the 
   // SETUP program must execute "set procedure ..." that
   // will open the file "dB2KTutorial.mnu" ... the class
   // will be available from that point on. This is
   // the classname of the menu:

   this.MenuClassName = "dB2KTutorialMenu"


Note that in the statement above: 'this.MenuClassName = "dB2KTutorialMenu"' -- there is NO SPACE between "Tutorial" and "Menu".

This code (or class) gets loaded into memory when the "app" object is created with the command "new TutorialApp()". There isn't much code in this class because this is class (TutorialApp) is subclassed from another class called "GenericMDIApp" (Which we still need to write). This subclassed object merely sets two custom properties. The rest of the work will be done in the super class (in GernericMDIApp).

We now need to write the code for GernericMDIApp so save the START program (<Ctrl>+S, name it "START").


The next file that we need to create is the generic application class.

In the command window type (or, in the navigator, double-click the "Untitled" icon under the Programs tab -- make sure when you save the file that you name it properly):

   create command

This will bring up a second window in the source editor (you can switch back and forth with the tabs ...).

We need to create a class. Interestingly enough, to create an object in dB2K that is not based on a stock object (one of the ones built-in to dB2K), the syntax is as simple as:

   class GenericMDIApp

This tells dB2K we are creating our own class, called "GenericMDIApp".

We can create properties and methods, but not events. For our purposes, we need at least two methods, one to handle opening the application, the other to deal with shutting it down (this should handle any cleanup necessary).

We also need to have some code that sets some custom properties, and does whatever other setup for the application object that is necessary. This code is executed for each instance of the object, when the object is instantiated (app = new ...). because it is placed before the methods we will be defining. This is called the "constructor code".

   // This custom property should be overwritten
   // in a subclass, or after the class is created, but
   // before the Open() method is invoked:
   this.FrameWinText = "Generic MDI application"

   // The same goes for this custom property:
   this.MenuClassName = "MyMainMenu"

   // We assume here that every MDI app will have
   // a SETUP.PRG
   do setup

   // Assign a property to _app.frameWin, which is
   // a reference to this object: "this". = this

The "Open" Method
For our purposes, the open method will not be real complicated. It will perform a few basic tasks -- turning off the application shell (see "shell()" in online help), turn off the standard dBASE toolbar and statusbar, and set the menu we created in the previous part of the tutorial as the current menu.

   Function Open
      // set a reference to the menu
      private c
      // build the command (a 'macro'):
      c = 'this.rootMenu = new '+this.MenuClassName+;
      // execute it:

      // Make sure no forms are open
      close forms

      // Make sure we're not in "design mode"
      set design off

      // set the <Escape> key "off" but store the 
      // current setting:
      this.OldEscape = set("ESCAPE")
      set escape off

      // Turn off the Visual dBASE shell, but 
      // leave the MDI frame window:
      shell( false, true )

      // Turn off the application's speedBar and statusBar
      _app.speedbar := false
      _app.statusBar := false

      // Set the text property for the application's framewin
      _app.framewin.oldText = _app.framewin.text
      _app.framewin.text    := this.FrameWinText

Note:The first three commands in the Open method shown above are used to create a "macro" command and execute it -- this is a way of building a program statement that needs to be executed but also needs to be very flexible. By building a character string, we can insert variable values (in this case, the name of the menu class), and then once the whole string is built, we can execute the command in one shot (&c.).

For more information on using macro expansion, see "&" in online help ...

Before proceeding we must address a bug that was mentioned in the Menu part of the tutorial. When our application opens, the above code attaches our menu to the application's main window, the WinFrame. However, for some reason when a form is opened in the WinFrame the menu disappears. To get around this problem we need to set the menuFile property for all the forms for which the the menu used. For whatever reason this works to get around the problem.

But wait, OOP makes this modification an easy task. We really only need to add this property to one custom form since all the child forms will inherit the property. Therefore, use the navigator and open DataForm.cfm in the designer. Bring up the Inspector and enter the text "db2ktutorial" in the menuFile property (and press <Enter> or this will not "take") OR click on the tool button and select "dB2KTutorial.mnu" from the dialog that appears. This should not be necessary, but ... We are not using the menuFile property on the BASE and DIALOG forms because some of the forms derived from these are modal dialogs and the menuFile property would place the menu on these forms.

The "Close" Method
This method will handle closing forms and cleaning up after the application, including resetting, if returning to the development environment, various changes that were made in the OPEN method above.

   function close
      // close any forms that might have been left open
      close forms

      // if we are in the "runtime" environment (the executable),
      // we want to "quit" (otherwise the framewin will
      // be left on screen and dB2KRun.exe will be left
      // in memory!)
      if ( "runtime" $ lower( version(0) ) )
         // otherwise we're in the IDE, let's reset some 
         // values:
         with ( _app )
    := null
            framewin.text := framewin.OldText
            speedbar      := true
            statusBar     := true

         // go back to design mode:
         set design on

         // set escape back to whatever it's previous
         // state was:
         cEscape = this.oldEscape
         set escape &cEscape.

         // close any open procedures
         set procedure to

         // release the menu

         // set the shell back ...
         shell( true, true )


ENDCLASS // don't forget this!

Note: The "with/endwith" construction is new to Visual dBASE 7.x, and is very useful when working with objects. You will see this code a lot if you examine code created by the form or report designers. The main purpose is to shorten your code -- see online help for details.

There is one caveat with the with/endwith construct -- you cannot create new (custom) properties of an object inside this code. You must create it outside the with/endwith construct.

Save this file and exit (<Ctrl>+W) ... this will leave the original START.PRG in the source editor. We need to add the application specific object here.

Create the Setup Program

The next thing we need to do is create a SETUP program. The reason for this program is that it is used in two ways -- 1) It is called from the GenericMDIApp to make sure that all necessary procedure files and other code is open (using the ADDITIVE clause) and available at all times; 2) When developing the application, you need a way to call all this same code.

Rather than putting the code in two different places, we put it in SETUP.PRG, and it is then available either by running the START program, or by simply running SETUP ...

Create a new program in the source editor (double-click the "Untitled" icon in the navigator under the "Programs" tab), and enter the following:


      The "setup program" for the MDI Tutorial
      application ... open any and all files
      needed to run the application and/or develop
      the application.

   // These always get me -- if the program
   // crashes, and they usually do while developing --
   // (due to programmer error), the speedBar and the
   // statusBar in the IDE is not available ...  this 
   // just puts them back. the MDI application class turns 
   // them back off ...
   _app.speedbar  := true
   _app.statusBar := true

   // this can also cause problems:
   set design on

   // Set procedures ...:
   set procedure to START.PRG additive

   // make sure the menu is available:
   set procedure to db2ktutorial.mnu additive

   // custom controls used by the application
   set procedure to ""   additive
   set procedure to ""   additive
   set procedure to ""   additive
   set procedure to ""   additive
   // add others as needed here:

Most of the forms also need to have their code available, just like a procedure or custom control file, so that we can just call them as needed. This is also done in the SETUP program. So before doing anything else, Add the following statements to the above file:

   set procedure to COUNTRY.WFM     additive
   set procedure to CUSTOMER.WFM    additive
   set procedure to INVENTORY.WFM   additive
   set procedure to INVOICE.WFM     additive
   set procedure to STATE.WFM       additive
   set procedure to SUPPLIER.WFM    additive
   set procedure to PREVIEW.WFM     additive

We could load and unload these procedure file in the menu, when the form is called. You may have noticed that is what we did with the report files. But that would mean that each time a form is called, the procedure file would need to load before the form is opened which will degrade the applications performance. When you proceed to developing your own application, I would suggest in general that you would want to load the procedure files for the forms that are used the most, and open/close the rest as needed. The advantage to opening all of the forms in the beginning when you call a form, it appears pretty close to instantaneously on screen -- speed is an issue for some folks. (Note that we do not need to do this for the InvoiceEdit and LineItemEdit forms as they are "dialog" forms that are only opened from the Invoice form.)

The setup program can also include any SET type commands, such as "SET EPOCH" or others (for the application you should consider storing that kind of "command" in the application's .INI file ... we'll look at that when we get to deploying the application).

When done entering your commands as shown above, save and exit the source editor (<Ctrl>+W, and enter "SETUP" as the name of the program).

Test the Start Program
We really ought to test this to make sure that it works ...

All you need to do is either double-click the "START.PRG" program icon in the Navigator, or type, in the command window:

   do start

Note: If you made mistakes (typos) typing code in, errors will occur as you execute the start program. Don't panic!

You may want to select the "Fix" button, and compare the code against what is shown here. The code here does work -- it was pasted in from a working application. Make corrections and save (<Ctrl>+W).

If you fixed the problem, the program will continue where it left off.

If you did not (or another error exists in the code), another error message will occur ...

At the very worst, you may get a GPF. Again, do not panic. There are some bugs in the dB2K error handling mechanism that may be causing this problem. Select the "No" button when asked about saving your work, and then restart dB2K. Run the "SETUP" program to restore the environment ...

Bring the START program, or the SETUP program, or the GenericMDIApp custom class file into the source editor and compare (again) against the listings here ...

You should see everything change, the menu should be the one we designed earlier, the titlebar should show the text we defined, and so on. One problem exists ...

Try selecting the "File" menu, and then "Exit" ... nothing happens. You're STUCK! Well, not really ... use the 'x' button in the upper right of the title bar (this will close dB2K) ... You will need to restart dB2K, and then run the SETUP program (this will reset the toolbar, and such).

Back To The Menu
We want to go back to the menu we created earlier, and add some code. The only code left to add is code that is executed when the user "Exits" the application. Otherwise the same thing will happen to you as above each time you test the application ...

We need to bring the tutorial menu back into the designer. To do that, click on the "Forms" tab, and right click on "dB2KTutorial.mnu" in the navigator. Select "Design Menu" or press <F2> to bring this up in the designer.

Click on the "Exit" menu object which appears under the "File" menu. In the inspector, click on the "Events" tab, and then on the onClick event. There is a "tool" button -- click that. This will bring up a source code editor window, and you can write code for the menu object's onClick event:

   function EXIT_onClick
   return ( )

You may want to press enter after the second line (one tester of this tutorial found it didn't work without that ...).

Press <Ctrl>+W to save and exit.

Re-testing the START Program
Let's try this again ... in the Navigator, click on the "Programs" tab, and double-click the program called "START".

Select the "File" menu, and notice the options. Try the other menus ...

Finally, select the "File" menu, and then the "Exit" option. This should bring you back to where you were ... much better than before.

Proceed to the next part of the tutorial: Building the Executable
Go back to the tutorial menu

The Legal Stuff: This document is part of the dB2K Tutorial created by Ken Mayer. This material is copyright © 2001, by Ken Mayer. dB2K is copyrighted, trademarked, etc., by dBASE, Inc., the BDE (Borland Database Engine) and BDE Administrator are copyrighted, trademarked and all that by Borland, International. This document may not be posted elsewhere without the explicit permission of the author, who retains all rights to the document.