Session E-C++

Using VFP objects, C++ objects and the Library Construction Kit

Calvin Hsia
Microsoft Corp.


INTRODUCTION

 

Visual Foxpro 3.0 brings the power of OOP to xbase. You've all heard that before, but what does it really mean? What is OOP and how can you take advantage of it? You have a boatload of existing customers using your Foxpro applications. How can you migrate easily to the new way of thinking?

 


The APPLICATION Class

All of your applications do some sort of initialization before startup, and follow some sort of shut-down process when exiting.

Foxpro objects provide methods called INIT and DESTROY that are automatically called when the object is created and when it's destroyed.

Try this

Try running this program, and variations of it. Experiment with closing the form and seeing when the Destroy method gets called.

We see that since the Destroy method is automatically called by Foxpro, the cleanup code will always be called. In fact, many methods get called automatically by Visual Foxpro, without user code explicitly calling it. Let's take advantage of this to transform our Foxpro 2.x application to OOP.

Now when the app terminates, the Destroy event will automatically get called, cleaning up the environment.

In the above example, we used the Custom base class. We could have used a class that you designed previously as a base template class for all your applications:

This will have MyApp inherit the Init and Destroy methods of MyAppClass. If there are changes to MyAppClass, they will automatically be inherited by MyApp, and any other objects based on MyAppClass.

An object in OOP can have properties and associated methods. Thus this technique can be used whenever you have some cleanup to do after some work is done. For example, you can have a SET object that remembers the SET status of a particular setting:

 

This class can be used to store the settings of may objects.

It's important to realize that there are 2 object hierarchies to deal with when using Visual Foxpro OOP. The Containership hierarchy means that one object is contained by another. A button can be contained by a pageframe, which itself is contained in a form. We can reference the button using dot separators: Thisform.pageframe1.mybutton. You can create containership hierarchies via the visual class designer or with program code, using a PRG.

The inheritance hierarchy shows the lineage of a particular object. The baseclass of each object in the inheritance must be the same. A button can only inherit from another button: it cannot inherit from a listbox. We can refer to the parent class using the double-colon syntax after the class name: Myclass::method.

We can use the same process of using a small sample PRG to find out how Error methods are invoked. Try adding a line that causes an error of some sort.

 

If an error occurs when executing code for a particular object, that object's Error method is called, if there is one. Errors do not propagate to the container object, but Error methods can be inherited.

If you want to have a single error method invoked if an error occurred on a contained object, you can. The easiest way to do this is to make a base class for each control (CommandButton, Listbox, etc.). The Error method for each of these classes can check to see if it has a Parent:

 

These few lines march up the containership hierarchy to get to the name of the highest level container.

Then we can just invoke the Error method of that object:

Once you've constructed your base classes and your error handler, you never have to worry about error handlers again.

 

How many levels of inheritance can we have in Visual Foxpro ? How can we determine that? We can write a small test program that emits a PRG that creates a large inheritance hierarchy:

 

How many objects can we have ? Let's write a simple program that creates a lot of objects. Create a class called T which lives in a VCX called T. Add a single commandbutton, and a single timer object. When we run the following program, 20 instances of the visual class are generated and displayed.

We can also so something similar with non-visual classes:

Visual Foxpro classes can also be used as structures. Structures are useful programming constructs that can hold related data together. A customer record can consist of Name, address etc. Each instance of a customer has it's own value of each of the fields associated with Customer. A record in a DBF can be thought of as a structure.

In C++, there is no difference between a Class and a Structure (except for access rights, like Public, Private, and Proctected). A C program that uses structures can use the CLASS keyword instead of the Struct keyword. This similarity between a class definition and a struct definition shows us how to implement structures in Visual Foxpro's OOP language.

In the next example, we have a program that will create a hierarchical tree of directory structure nodes.


The Library Construction Kit

 

The Library Construction Kit (LCK) that comes with the Professional Version of Visual Foxpro allows you to write C and C++ programs to extend the capabilities of your Foxpro programs. You can directly control hardware, improve performance, or do other tasks not possible within a Foxpro program.

The LCK consists of only 2 necessary files: PRO_EXT.H which defines the LCK to the Visual C compiler, and WINAPIMS.LIB, which is the library file that contains the interface code to Visual Foxpro. Other files are included as samples. To use it, you'll need Visual C 2.x.

An FLL is a DLL that includes two Foxpro specific structures. A DLL is the same as an EXE file, except that it can't run by itself. It contains routines and data that can be used by any number of clients.

Welcome to the 32 bit world

Visual Foxpro is a 32 bit product throughout. Foxpro 2.x for Windows is also a 32 bit product internally, but it was written for the 16 bit Win API, which means that 2.x FLLs are 16 bit.

A 16 bit application can only address 2^16, or 65536 bytes directly. It uses the microprocessor in 16 bit mode, where each machine register is 16 bits wide. In order to handle applications that require more memory, Intel implemented a segmented addressing scheme, whereby memory is addressed in 64K segments. This limitation makes it much more difficult to develop programs, which have to handle memory addresses as pairs of 16 bit numbers.

A 32 bit application uses the microprocessor in 32 bit mode, which means that all registers are 32 bits wide. Directly addressing 2^32, or 4 Gigabytes makes it much easier to write complicated applications.

All FLLs that worked with 2.x will have to be recompiled to work with Visual Foxpro. The LCK has not changed much from 2.x, so many libraries will work unmodified after recompilation.

For your first FLL, create a file called LCK.CPP with these contents:

 

Start MS VC and Choose FILE\NEW\Project from the VC main menu and choose to create a DLL. Add LCK.CPP to your project.

Change the following settings:

If everything goes well, you've successfully created your first FLL!

Every FLL must #INCLUDE the file PRO_EXT.H, and every FLL must have the last two structures in LCK.CPP. The first structure FoxInfo is a list of all functions in the FLL that will be callable from Visual Foxpro. It also specifies the corresponding C function name, number and types of parameters passed to each function.

The second structure FoxTable has a single entry which points to the FoxInfo structure and indicates the number of entries to be found in there.

If you name your program with a .C extension, the C compiler will treat it as a C program. The .CPP extension indicates a C++ program. Since Visual Foxpro expects the FoxTable structure as a C exported name, 'extern "C"' is necessary to tell the compiler to name the FoxTable structure according to the C rules, rather than the C++ rules.

The FoxInfo structure for our sample indicates that our function, MYFUNC(), can take up to two parameters, of any type. If a funtion were to take 3 parameters, 2 of which were required, it might look something like:

The letters indicate the type of parameter passed (Character, Date), and the "." precedes the optional third numeric parameter.

Foxpro variables within an FLL are represented by a Value structure, which is defined in PRO_EXT.H as:

Not all the fields in the Value structure are used simultaneously. The ev_type member contains the type of the variable, as returned by the TYPE() function: C for character, etc. So, if ev_type = 'I', then the value of the variable is in ev_long.

Character variables are a little more complex. When the ev_type = 'C', the ev_handle is a handle to the string. A handle is just a number, which is used as an index into a table to find the address of the string. Since strings can be of any length, they can take quite a bit of memory, and that memory can be moved around or swapped out to disk.

To obtain the actual string, first you need to lock the handle using _HLock(), then call a function to convert the handle to a pointer _HandToPtr().

When a parameter is passed by reference, the Locator structure is used:

NTI is an index into the Name Table, which holds information about user symbols. If it’s an array, then l_subs indicates the number of subscripts. If it’s an array element, then l_sub1 and l_sub2 indicate the subscript values.

The _Load() and _Store() functions allow your FLL program to read and write values directly into Foxpro variables. They take a Value and a Locator as parameters, and load or store one into the other.

There are dozens of other callback functions that allow your FLL program to call back into Foxpro to accomplish tasks. For example:

 

You could write your whole app in C, making callbacks!

You can use VC to examine Foxpro variables very easily. Change "Project\Settings\Debug\Executable for Debug session" to c:\fox30\vfp.exe or wherever you've installed VFP. You can specify the working directory and additional program arguments from the same options dialog.

Highlight a line of source code in your FLL and hit the F9 key which sets a breakpoint on that line. Choose Debug\Go. A dialog correctly indicates that VFP.EXE doesn't contain debug information. Since we're not debugging VFP.EXE, which has no bugs in it, choose to continue. When execution reaches your breakpoint, VC automatically comes to the foreground, and you can inspect variables, memory, registers, the call stack, etc.

 

Executing C code is inherently much faster than executing Foxpro code. In fact, every Foxpro statement causes multiple lines of C code to be executed. Visual Foxpro itself is written in C and C++, as is the majority of commercially available software, including operating systems like Windows.

Foxpro allows you to call C code easily. If it's your own C code, you can use the LCK. If it's C code inside another DLL, you can use Foxtools or the DECLARE DLL command. However, sometimes it's easier to call C code from within a C program. For example, your PRG can call WIN API routines and pass parameter types that Foxpro supports, but passing structures is not as easy as in C.

Here's a simple example. A graphics program allows the user to draw lines and set pixels on a form. Suppose you want a program that will paint an area delimited by black pixels to be filled in black. Given a two dimensional array of pixels which represent the bitmap, and a single point within a shape in the bitmap, how do we toggle the pixels within the shape so as to do an area fill?

This is a great interview question, and it turns out there's an elegant 8 line solution.

This recursive algorithm makes many calls to itself, and thus eats up a lot of stack space. If the line to be filled contains 100 pixels that are not filled yet, the program will call itself 100 times.

Visual Foxpro allows 128 DO levels, so this algorithm will only work for small areas. Using the LCK, creating an example that correctly fills areas is pretty straightforward, and it executes much faster than the non LCK solution. Not only do we get a performance gain, but it's impossible to run the algorithm as written with VFP's stack limitation.

Modifying the program to keep track of maximum stack depth, moderately sized shapes to fill can take tens of thousands of recursive stack levels!

The sample program below creates and displays a form. Two simple shapes are added to the form to create outlines bounding areas to be filled. There are 2 fill routines called for comparison: 1 in Foxpro and 1 written via the LCK.

The FillX method is a recursive method that just implements the simple area fill algorithm above. It’s called first to fill a small box. Because of Foxpro’s limit on the number of DO levels, this routine can’t be used to fill very large areas without running into stack overflow problems.

The Fill method calls the dofill C routine in the LCK below. It also implements the same recursive fill algorithm above, but it’s written in C and will use the C stack, which is limited only by memory.

The other methods such as MouseDown, etc. are used so you can use your mouse to draw your own shape on the form to be filled. Shift-Click in the center of an area to be filled, and the DoFill algorithm will be called. Summary statistics on the number of recursive levels used and the time taken are displayed as well.

Beware that you don’t try to paint an unbounded area: there is no bounds checking and your program will go off to never never land.

In the FLL, there are actually two functions to do the Fill: one called Fill, and the other called FillFast. The first does the same as the PRG: it calls the VFP PSET and POINT methods to set and get pixel values.

FillFast bypasses VFP altogether and uses the WIN API to Get and Set the pixels directly, and is much faster. However, it doesn't respect things like the object's foreground/background color for the fill. This demonstrates that even though it’s written in C, a program won’t necessarily be faster.

Another example of a gain in performance from the LCK is in number crunching. A perfect number is defined as a number N that is equal to the sum of all of its divisors. The first part of this VFP program calculates the perfect numbers below 1000 in 13 seconds on my Pentium 90 under NT. The second part uses the LCK and gets the same results in less than 1 second.

Note: in order for this sample to work, the FLL must be written in C++, and not in C, because the code takes advantage of some of the C++ extensions to C, such as in line declaration of variables.

 

The below samples show how to integrate C++ classes into an LCK module. Strings are a common sample class when dealing with C++, and for the LCK, it's a great example of how to create a class that is very useful and it hides the complexities of string handling.

A Foxpro string is represented in a Value structure, with the ev_type = 'H' and the ev_handle being a handle (an identifier) with which to refer to the string. The ev_length member is the length of the string. For a C program to access this string, the handle has to be locked, using _HLock(), then converted to a pointer, using _HandToPtr(), and then unlocked. Creating a new string requires a call to _AllocHand to get a new handle.

The CString class in the sample below is a very simple string class with just 2 members: m_len (the length of the string) and m_ptr (a pointer to the string).

The CLckString class inherits from the CString class, and has some more useful members and member functions (methods, in VFP parlance). Some of the basic methods include multiple constructors for the various ways to initialize a string, and various operator overloads, so we can easily write MyStrobj1 = MyStrobj2 + MyStrobj3, and have it work.

Fancier methods are GetFPVar and PutFPVar, which will get a Foxpro variable name and store the string value into a CLckString object, or take the string object and put it into a Foxpro variable name.

The Foxapp class is instantiated at FLL load time, even before the CALLONLOAD function Initialize. This can be verified by placing a breakpoint on the Initialize function and the Foxapp constructor function. This class has a single member function Executef, which will execute any single line Foxpro function. You can add more member functions to this framework.

There are eight new LCK functions in Visual Foxpro that were added to support the new Object type. The Professional Edition of Visual FoxPro contains the following additional API routines:

 

These are described in the Readme.hlp file. The Value structure was also changed to accomodate object references as parameters.

 

You can use the DECLARE DLL command to call most of the thousands of Win API functions or any other functions that are exported by DLLs. The professional version of Visual Foxpro includes a help file called WIN32API.HLP which documents the Win32 API. For example, you can call the Win32 API directly to find the title of the currently active window:

 

The GetUserName function returns the name of the user currently logged into the system.

Here's a way to bring another application to the foreground:

Using the "win32api" keyword in the Declare DLL command is a shortcut. Normally, a user has to know in which DLL a particular function resides. The Win32api functions reside in several DLLs: Kernel32.DLL, GDI32.DLL, etc. The Win32API shortcut tells VFP to search through all these DLLs, and thus keeps the user from having to know in which DLL the function lives. This is especially useful since although there is plenty of documentation for the Win32API, none of it indicates in which DLL a function resides.

The new Declare DLL command is quite powerful, but you can't call 16 bit DLLs with it. You can use Foxtools to call 32 or 16 bit DLLS, using the RegFN() and CallFN() functions. There's a new function called RegFN32(), which registers a 32 bit DLL. RegFN() registers a 16 bit DLL

Another limitation of the DECLARE DLL and Foxtools way of calling DLLs is that functions that require complex parameters may be difficult or impossible to call from within VFP directly. These parameters might be structures of arrays, arrays of structures, pointers, or some combination of these. Calling them from within an FLL is easy.

If you attend my OCX session, you’ll see how you can extend Visual Foxpro using C and C++ via OCXs very easily. An FLL is specific to Visual Foxpro, but an OCX can be used in Visual Basic, Visual C++, and any other language that can host an OLE Custom Control.

The LCK allows Visual Foxpro users to extend their apps in many ways. We’ve looked at performance, hardware control, and access to DLLs with complex parameters. Using VC with the LCK makes it easy to write C and C++ programs to explore this very powerful programming tool.