Developing a Database Application:
The dBASE™ PLUS Tutorial

Ken Mayer, based on work by Michael Nuwer and Ken Mayer


Phase VIII: Creating the Startup Program


  Back to the tutorial menu     Back to previous part of tutorial: Creating the Menu   Proceed to the next part of the tutorial: Building and Deploying the Executable 

Goals and Objectives

The goals for Phase VIII of this tutorial project are:

Additional Reading

The Application as an Object

The approach that we will adopt in our sample application is to create 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.

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 "[New Program]" 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:

            /*
                START.PRG
                Author: Ken Mayer
                Date  : January 13, 2017
         
                Tutorial project START program
         
                This program is used to start the TUTORIAL 
                application. 
         
                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.
            */

We are 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 dBASE™ PLUS, 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":

               //update this for application version changes:
               #define AppVersion "4.0" // Version 4 of this tutorial project
              
               set talk off
               
               // set the private directory for the BDE
               // uses BDE (IDAPI) API Code below the TutorialApp
               // definition -- this uses a default folder,
               // no need to pass a value:
               setPrivateDir()
   
               // set up the application object:
               local app
               set procedure to GenericMDIApp.cc
               app = new TutorialApp()
               app.open()
            return 

Save this as start.prg

The API code shown should not be hand-typed into a program, you should copy and paste it to avoid typos. Trying to debug API code can be a bit difficult.

Using Windows File Explorer, create a folder from the root of your drive, and call it "tempfiles".

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 app.open() method calls code we must 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 -- a subclass of another class:

            /*
               TutorialApp is a subclass of the "GenericMDIApp" contained
               in the .cc file of the same name:
            */
            class TutorialApp of GenericMDIApp
               // set any specific code for the subclassed
               // MDI application here - note this uses the AppVersion constant
               // created above for the version number:
               this.FrameWinText = "dBASE™ PLUS Tutorial Project -- vers. " + AppVersion
            
               // note this is not the file name -- the 
               // SETUP program must execute "set procedure ..." that
               // will open the file "Tutorial.mnu" ... the class
               // will be available from that point on. This is
               // the classname of the menu:
               this.MenuClassName = "TutorialMenu"
         
            endclass

Note that in the statement above: this.MenuClassName = "TutorialMenu" -- 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 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 GenericMDIApp).

The last part of the start program is the function used to force the BDE to write the _QSQL files to a standard folder for that purpose. This is something that it is recommended you just copy and paste, API code is tricky and misspelling something can cause frustrating results:

            /*
                  Code that deals with setting the private folder for the BDE
                  to write _QSQLnnnn.dbf files, this is API code and a bit involved.
                  Provided by Wian in the dBASE newsgroups, but appears to have
                  been originally provided by Rick Miller.
               */
            function setPrivateDir( cPath )
               if not type("DbiSetPrivateDir") == "FP"
                  extern CUSHORT DbiSetPrivateDir(CSTRING) idapi32 from "DbiSetPrivateDir"
               endif
            
               Local cDir
               if type("argVector(1)") == "C"
                  cDir  =  cPath
               else
                  // when cDir == null, reset to default.
                  cDir  =  null
               endif
            return iif( DbiSetPrivateDir( cDir ) == 0, true, false )

We now need to write the code for GenericMDIApp so save the START program (<Ctrl>+S, name it "start" if you did not save it previously). It is okay to leave the Source Editor open for this.

Create GenericMDIApp.cc

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 "[New Program]" icon under the Programs tab -- make sure when you save the file that you name it properly):

   create command GenericMDIApp.cc

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 dBASE™ PLUS that is not based on a stock object (one of the ones built-in to dBASE™ PLUS), the syntax is as simple as:

            /*
               GenericMDIApp.cc
            
               Author: Ken Mayer
               Date  : January 13, 2017
               
               An application class object designed to handle the setup and shut down of 
               an application using MDI (Multiple Document Interface).
               
               This code should be subclassed for a specific application. 
               As this is part of the tutorial project for dBASE™, see the start.prg
               file.
            */
            class GenericMDIApp

(Ignoring the comments ...) This tells dBASE™ PLUS 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 ...). 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".
            _app.framewin.app = 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+;
                   '(_app.framewin,"Root")'
               // execute it:
               &c.
         
               // 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 dBASE™ PLUS shell, but 
               // leave the MDI frame window:
               shell( false, true )
         
               // Turn off the application's speedBar, statusBar, and tabBar
               // however, for a true MDI app, you may actually want
               // the tabBar turned on:
               _app.speedbar  := false
               _app.statusBar := false
               _app.tabBar    := false
               
               // Set the text property for the application's framewin,
               // but keep a reference to the original text so we can re-set it
               _app.framewin.oldText = _app.framewin.text
               _app.framewin.text    := this.FrameWinText
            return

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 dBASE™ PLUSRun.exe will be left
               // in memory!)
               if ( "runtime" $ lower( version(0) ) )
                  quit
               else
                  // otherwise we're in the IDE, let's reset some 
                  // values:
                  with ( _app )
                     framewin.app  := null
                     framewin.text := framewin.OldText
                     speedbar      := true
                     statusBar     := true
                     tabBar        := true
                  endwith
         
                  // 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
                  close procedure
         
                  // release the menu
                  _app.framewin.root.release()
         
                  // set the shell back ...
                  shell( true, true )
               endif
            return
         
         endclass // don't forget this!

Save this file and exit (<Ctrl>+W) ... this will leave the original start.prg in the source editor.

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 "[New Program]" icon in the navigator under the "Programs" tab), and enter the following:

            /* 
               SETUP.PRG
         
               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
            _app.tabBar    := true
         
            // this can also cause problems:
            set design on
         
            // Set procedures ...:
            set procedure to start
         
            // make sure the menu is available:
            set procedure to Tutorial.mnu 
         
            // custom controls used by the application
            set procedure to :FormControls:seeker.cc
            set procedure to :ReportControls:report.cc
            set procedure to :DT_Custom:MyControls.cc
            set procedure to :DT_Custom:CustomReportControls.cc
            // 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 
            set procedure to customer.wfm
            set procedure to inventory.wfm
            set procedure to invoice.wfm
            set procedure to state.wfm
            set procedure to supplier.wfm
            set procedure to preview.wfm

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, we suggest that you load the procedure files that are used the most, and open/close the rest as needed. The advantage to opening all of the forms in the beginning is that 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.)

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

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 dBASE™ PLUS). You will need to restart dBASE™ PLUS, 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 "Tutorial.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 ( _app.framewin.app.close() )

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.


  Back to the tutorial menu     Back to previous part of tutorial: Creating the Menu   Proceed to the next part of the tutorial: Building and Deploying the Executable