Share via


Visual FoxPro 7.0: Program Your Data with Powerful New COM, XML, and Web Services Support

MSDN Magazine

Visual FoxPro 7.0: Program Your Data with Powerful New COM, XML, and Web Services Support

Erik Moore
This article assumes you're familiar with COM, XML, and Visual FoxPro
Level of Difficulty     1   2   3 
SUMMARYVisual FoxPro 7.0 represents a significant improvement over version 6.0. There are many new features designed to support COM, XML, and Web Services. Now COM servers built with Visual FoxPro are more flexible and robust thanks to strong typing and the ability to implement interfaces from other type libraries. IDE features like the new object browser combine convenience and efficiency, and other language features such as event handlers and early binding to COM objects increase performance. Lastly, an enhanced session class plus several new XML functions make Visual FoxPro a great choice for Web application development.

T he latest version of Microsoft® Visual FoxPro® provides a set of new features that make it a better choice than ever for writing desktop and enterprise applications. The Visual FoxPro team listened to developers who wanted to build applications with Visual FoxPro that offer high-caliber features, and responded with enough goodies to give developers a way to do just that. All the new enhancements to the IDE, including perhaps the best IntelliSense® that Microsoft has ever released in any product, make using Visual FoxPro 7.0 much more pleasant. But it's the new COM and XML features that really make version 7.0 worth the upgrade. This article will explore the new capabilities of Visual FoxPro 7.0 and show how you can use them today.
      In this article, I'll begin with all of the new features designed to make Visual FoxPro COM servers more flexible and robust, and then talk about some new native XML functions that will help make constructing n-tier applications with Visual FoxPro painless. Finally, I'll show how all these new features helped make it possible for the Visual FoxPro team to build Web Services support right into the product.

COM in Visual FoxPro

      Creating a COM server in Visual FoxPro is extremely easy. A simple Visual FoxPro class can be compiled as a COM server after setting its OLEPUBLIC attribute. For classes defined in .prg files, this is done by adding the OLEPUBLIC keyword to the DEFINE CLASS clause, as you can see in the following code.

  DEFINE CLASS HelloWorld AS Session OLEPUBLIC
  
FUNCTION Echo
ENDFUNC
ENDDEFINE

 

For classes defined in .vcx files, you simply check the OLEPUBLIC box in the Class Info dialog from the class designer (see Figure 1).

Figure 1 Class Info Dialog
Figure 1 Class Info Dialog

      Visual FoxPro version 3.0 introduced COM support with the ability to use ActiveX® controls in Visual FoxPro forms. Version 5.0 added the ability to create both in-process and out-of-process COM servers from Visual FoxPro classes. Visual FoxPro 6.0 added more capabilities in the form of better COM server error handling (COMRETURNERROR), more flexible array handling, better type libraries, and DCOM support. Service Pack 3 added its own set of COM capabilities, including support for multithreaded DLLs, a dedicated lightweight runtime, and a new lightweight Session class. At this point in the game, developers working with Visual FoxPro had it pretty good. They could build fast servers that worked well with Microsoft Transaction Server packages and COM+ applications, and worked when called with either early or late binding. Microsoft also released an add-on to the product, the VFPCOM tool, which is a separate component that allowed developers to bind events to another COM object.
      However, there were still a few gaping holes in the Visual FoxPro COM arsenal when comparing it to other Visual Studio® tools. With the introduction of Windows® 2000 and COM+ Services, developers quickly started investigating the new COM-based systems in Windows and discovered that there were areas of the COM+ sandbox that you couldn't play in. For example, loosely coupled events require that you construct a COM server that implements a published interface, and Queued Components requires the use of write-only properties. Visual FoxPro also couldn't instantiate a COM object that didn't support IDispatch, and couldn't bind to events of a COM server without distributing a helper object.
      The new COM features in Visual FoxPro 7.0 fill these holes. In the next sections I'll talk about some of them, including support for strong typing, specifying extended attributes for properties and methods, and implementing interfaces. There's also an extensible object browser, native error handling, and early binding for COM objects.

Strong Typing

      Visual FoxPro has never been a strongly typed language. That is, a Visual FoxPro variable doesn't care if it carries a numeric value one moment, and a character value the next. Code like the following is completely legal:

  x = "Howdy"
  
x = 68

 

      This being the case, the language doesn't even contain commands that declare a variable as a certain type. In Visual FoxPro it's also possible to write a function and call it like this:

  =SayIt("Howdy")
  
=SayIt(68)

FUNCTION SayIt(x)
MESSAGEBOX(TRANSFORM(x))
RETURN .T.
ENDFUNC

 

This code creates a single function that can take any type of data as a parameter and show a message box that displays the value of that data. The fact that all Visual FoxPro variables and parameters are a sort of internal variant and all parameters are mapped as variants is apparent when you examine the type library of a Visual FoxPro 6.0 COM object. The parameters and return values for every method are shown to be of type Variant. Since the language didn't allow you to specify parameter types, the Visual FoxPro compiler couldn't guess the type of parameters, so it labeled all parameters and return values as being of a generic type, Variant, that can hold any value.
      The new version of Visual FoxPro has the ability to strongly type the parameters and return values of methods. Now when you build a COM object that accepts parameters of a specified type, it will be evident to the calling application because the type of parameters will be depicted in the type library. The syntax for specifying parameter types in a Visual FoxPro class looks like this

  FUNCTION MyFunction(p1 AS String, p2 AS Integer) AS Boolean
  

 

where the first parameter is of type String, the second parameter is of type Integer, and the return value of the method is of type Boolean.
      The only new syntax here is the AS clause. Any or all parameters can be typed by following them with AS followed by the type, and the return value of the method is denoted with the last AS clause outside the parentheses. Keep in mind that this typing has no effect on your class internally; it's only used at compile time when Visual FoxPro builds the type library for your object.
      With strong typing, the previous function could be written in Visual FoxPro 7.0 this way:

  FUNCTION SayIt(x AS String) AS Boolean
  
MESSAGEBOX(TRANSFORM(x))
RETURN .T.
ENDFUNC

 

You would still not receive an error when passing it a numeric parameter from an internal routine instead of a character parameter. However, when this function is defined as a method of an object (as part of DEFINE CLASS) and compiled into a COM server, the type will now specify the types of all parameters and return values.
      Types available for most of the simple COM data types are shown in Figure 2. As you can see, strong typing in Visual FoxPro was implemented specifically to give the developer more control over the construction of the type library at compile time. But the Visual FoxPro team didn't stop there—they also added a couple of things that are designed for the same purpose.

Attributes for Properties and Methods

      The _COMATTRIB directive of DEFINE CLASS allows the developer to specify characteristics of object properties and methods to be carried forward to the type library. For instance, in Visual FoxPro there is no such concept as a write-only property, as there is no way to specify a property as write-only. The _COMATTRIB directive allows the developer to specify these and other characteristics for properties and methods in the type library.
      A _COMATTRIB property is actually a five-element array, with each element specifying an attribute for the property or method, as shown in Figure 3. The first element of this array is used to specify the accessibility of this property or method from a client. The flags can be assigned in any combination, and are specified by summing the values of the flags to be assigned. The valid flag values are listed in Figure 4.
      As an example, to specify that a property called MyProperty should be non-browsable and read-only, you would create a _COMATTRIB line such as this:

  DEFINE CLASS MyObject AS Session OLEPUBLIC
  
MyProperty = ""
MyProperty_COMATTRIB[1] = ;
COMATTRIB_READONLY+COMATTRIB_NONBROWSABLE
ENDDEFINE

 

      The second element of the _COMATTRIB array lets the developer specify a help string for the property or method. The help string is displayed in object browsers and IntelliSense and assists with external documentation for the properties, events, and methods. A method called CustomerFind might have a help string that reads: "Returns a customer object that meets the specified search criteria."
      Visual FoxPro does not, by default, preserve the case of custom properties. A property name entered as CustomerId will appear in the type library as CUSTOMERID. With the third _COMATTRIB element, the programmer can specify a case-sensitive string that will be used for the property's name in the type library. Note that methods defined in programmatic classes preserve their case in the type library automatically.
      The fourth element specifies the type of data that the specified property will hold (this one is not valid for methods). Use one of the values listed in Figure 2 as a type.
      The last element, which is valid for methods only, specifies the number of required parameters. If the number of parameters specified in this property is fewer than the number of actual parameters for the method, the remaining parameters are shown to be optional.
      To demonstrate using _COMATTRIB to its fullest, let's pretend you have an Account object with one property (AccountNo) and one method (Withdraw). The AccountNo property should be read-only, should be defined as character, should have a descriptive help string, and should appear in the type library with its case preserved. The Withdraw method should also appear with a descriptive help string and appear in the type library with its case preserved, and should specify that it requires one parameter. You could define this object with the code that is shown in Figure 5.
      Also note that if only the first COMATTRIB element is needed, there is no need to dimension the array and specify the rest of the elements. The flags value can be assigned as if _COMATTRIB were a simple property:

  AccountNo_COMATTRIB = COMATTRIB_READONLY
  

 

      For methods, if the only extended property that needs to be assigned is the help string, there is a shortcut for this, too. HELPSTRING is its own clause that serves the same purpose as the third element of the COMATTRIB array. In the example shown in Figure 5, the help string could have been assigned to the Withdraw method simply like this:

  FUNCTION Withdraw(nAmount AS Number) AS Boolean ;
  
HELPSTRING "Withdraw the specified amount from the account"
ENDFUNC

 

      All this power over type libraries may not sound like an impressive feature by itself. After all, Visual FoxPro-generated COM servers worked just fine with variants as parameters, and there was always external documentation for methods instead of help strings. But when you realize the functionality that an accurate and descriptive type library can buy you, you might be more impressed. For example, having write-only properties is required if you want to use Queued Components in COM+ applications or return value types equal to VOID. And now with strong typing, you can implement interfaces from other type libraries, a feature that itself opens up all kinds of possibilities.

Interface Implementation

      Now that Visual FoxPro has the ability to strongly type parameters, return values, and properties, and can specify extended attributes of properties and methods, it's possible for a Visual FoxPro class to implement an interface defined in another type library. When a class implements an interface, it is publishing a set of specifications for itself—a contract, if you will, that says it will conform to at least the specifications laid out by the definition of the interface. A Visual FoxPro class can implement an interface with the new IMPLEMENTS keyword in DEFINE CLASS:

  IMPLEMENTS cInterfaceName [EXCLUDE] IN TypeLib | TypeLibGUID | ProgID |
  

 

The class definition in Figure 6 implements an interface that will allow it to bind to events of the Word.Application object.
      There are several places in Windows-based programming where an object has to implement an interface to be useful. For example, with loosely coupled events in COM+ applications, a subscriber object has to implement an interface defined in the type library of a publisher object. When using the MSXML3 SAX parser, an object has to implement three interfaces defined by the parser type library. Also, an Office XP smart tag recognizer has to implement several interfaces to be callable from Office documents. (A smart tag recognizer is a custom component written to recognize custom tags; the interface is fully extensible by any third-party.)
      Implementing an interface requires that the object builder match every requirement of the interface. There is no such thing as implementing part of an interface, and if you try, Visual FoxPro will issue an error at compile time as it compares the signatures of your properties and methods with the signatures of the interface or interfaces that your object claims to implement. Even if your object doesn't do anything in the implemented methods, those methods still have to be defined, and must accept the same number and type of parameters. If you look at the code in the Visual FoxPro 7.0 samples, you might think that implementing an interface could be a lot of work. Depending on which interface you want to implement, there are potentially a lot of methods to write, even if you don't care about most of them. Luckily, the authors of Visual FoxPro were sympathetic to this fact and provided a cool new tool to help when you need to implement an interface.

The Object Browser

      In the past, multilingual programmers who used Visual FoxPro and dealt regularly with COM objects might have found themselves often opening up Visual Basic® to use its Object Browser. The Visual Basic 6.0 Object Browser is a great tool that allows the developer to open a DLL or a typelib file and view all of the interfaces and classes and their properties, events, and methods. The Class Browser in Visual FoxPro 6.0 supplied some of this functionality, but the information displayed was sometimes cryptic and incomplete. The Visual FoxPro 7.0 Object Browser (see Figure 7) is a vast improvement over both the Visual FoxPro 6.0 Class Browser and the Visual Basic 6.0 Object Browser.

Figure 7 Visual FoxPro Object Browser
Figure 7 Visual FoxPro Object Browser

      The Visual FoxPro Object Browser looks a lot like the Microsoft .NET Object Browser, except it's extensible with Visual FoxPro classes. A special add-in class is provided that can be subclassed and registered to handle events raised by the Object Browser, so your custom code can run when you drag an item from the browser. Out of the box, Visual FoxPro comes with one very useful Object Browser add-in that generates the code to implement an interface when an interface is dragged to a code window. The code in Figure 8 was generated by dragging the IVBSAXContentHandler interface to an empty code editor window. As you can see, this handy little feature has the potential to save you a lot of work.
      A word of warning when using code generated by the interface add-in: when you drag an interface to a code window to create code, the IMPLEMENTS keyword uses the file name of the type library to specify the location of the interface definition. Using the file name creates a dependency that you might not want. When the class is instantiated, the type library must be in the Windows path for the interface definition to be resolved.
      The preferred method of specifying the location of the interface definition is using the TypeLibID, so that the object factory can resolve the physical location of the type library by looking up its entry in the registry. So if you use the Object Browser add-in to generate interface implementation code, consider replacing the hardcoded path to the type library with the type library's GUID, or at least the ProgId of an object that is defined in the same type library.

Event Handling

      Event handling is a fundamental programming concept in which one object raises events that another event responds to. An object that handles events for another object is called an event handler (sometimes called an event sink), and the act of connecting an event handler to an object that raises events is called binding. Many languages, even most scripting languages, provide for object binding like this, but up until now Visual FoxPro did not provide a native way to do this. The VFPCOM utility that appeared on the Web site for Visual FoxPro some time after the release of Visual FoxPro 6.0 allowed Visual FoxPro 6.0 objects to be bound to the events of other objects, but required that you ship a separate DLL.
      The new EventHandler function in Visual FoxPro 7.0 makes this functionality native. EventHandler allows the developer to bind a native Visual FoxPro object to a COM object that raises one or more events. It's really easy to use. The syntax is as follows:

  EventHandler(oCOMObject, oVisual FoxProObject [, lUnbind])
  

 

The first parameter is a reference to a COM object that raises one or more events, and the second is a reference to the Visual FoxPro object designed to sink those events. The third parameter (which is optional) is used only after all the work is done to unhook the handler from the COM object.
      As a simple example, the code in Figure 9 shows how you can use the new EventHandler function to show a user an instance of Word, and be notified when the user closes the instance.
      Event handling has a lot of potential uses. You can bind to the events of an Outlook® instance to be notified when the user sends e-mail, or you can bind to the events of an ADO recordset to run specific code when the record pointer moves or when a large recordset is finished loading, giving your application the appearance of being multithreaded.

Early Binding

      Another significant new feature in Visual FoxPro 7.0 is the capability to create or acquire an early-bound reference to an external COM object. Early-bound COM objects have a performance advantage over late-bound objects because intimate knowledge of the server is compiled into the client instead of being acquired at runtime. The decision to use early binding requires consideration of a trade-off. If a server is recompiled and its signature changes, late-bound clients will still work as long as the methods they call and their parameters are still supported, but early-bound clients will break. On the other hand, if the server is stable, and not likely to be recompiled during the lifetime of the client, there's little risk of early-bound clients breaking due to changes in the interface.
      Visual FoxPro 7.0 supports building early-bound clients with the extension of an existing function, CREATEOBJECTEX, and the addition of a new function, GETINTERFACE. CREATEOBJECTEX now takes an additional parameter that specifies the GUID of the type library (cIID) that can be used at compile time to resolve function pointers:

  CREATEOBJECTEX(cCLSID | cPROGID, cComputerName [, cIID])
  

 

Using CREATEOBJECTEX to create an early-bound instance of an object is no more difficult than creating a late-bound instance with CREATEOBJECT:

  oXL = CREATEOBJECTEX("Excel.Application","","")
  

 

      Since CREATEOBJECTEX is also used for creating a DCOM object that runs on another machine, there's a second parameter that you won't use for early binding. The parameter is not optional, but if you pass an empty string, the object will run on the local computer. The third parameter (the interface ID) is optional, but you need to pass at least an empty string to get early binding. If you leave off the parameter entirely, your object will be late-bound. You only need to pass a value to the third parameter if you want to bind to an interface other than the default interface, in which case you'll pass that interface's cIID. You can look up an interface's cIID with a typelib viewing tool such as the OLEView.exe tool that ships with Visual Studio 6.0.
      If the object you want to call using early binding was not created with CREATEOBJECT, but was passed as a parameter to one of your procedures or returned from a factory method of another COM object, you can use the new GETINTERFACE function to get an early-bound reference to that object. The syntax for GETINTERFACE is as follows:

  GETINTERFACE(oObject [, cIID | cInterface[, cTypelib | cProgID]])
  

 

Calling methods on an early-bound object uses the same syntax as calling methods on late-bound objects so, where applicable, COM clients written in Visual FoxPro 6.0 and earlier can easily be retrofitted to use early binding.
      Except in heavily used or performance-sensitive applications, it might be difficult to detect performance gains made by using early binding, and it's entirely possible that early binding won't help at all. For example, early binding to the DomDocument object in MSXML won't usually buy you any performance gains because most of the operations when parsing XML are performed on many different objects returned by the parser, and not on the DOM object itself. This new feature provides the biggest performance boosts in situations in which many calls are made on a single COM object, as in a tight loop.
      Besides the big ticket items in the Visual FoxPro arsenal of COM improvements that I've just described, the Visual FoxPro team threw in lots of little goodies that make working with COM in Visual FoxPro easier. Some, like the minor changes in the default behavior of the Session class, the ability to return arrays from methods, and new COM support for the AMEMBERS function, are small but appreciated. Let's look at those new features next.

Session Class Changes

      The Session base class, added in Visual FoxPro 6.0 SP3, is designed to be the base class of choice when building COM objects in Visual FoxPro. The Session class provides a private datasession without the overhead of a form (which was formerly the only way to get a new datasession). This allows data encapsulation when several objects collaborate. Before the inception of the Session class, there was an ongoing controversy over which was the best base class to derive COM servers from; among the candidates were Custom, Line, and Relation, all of which were suitable. With the new improvement to the Session class, the controversy has ended with the Session class the clear winner.
      In Visual FoxPro 6.0, unless you specifically declared each intrinsic property as hidden they would be displayed in the type library, whether or not they were relevant to the operation of the class. The Session class in Visual FoxPro 7.0 hides all intrinsic properties by default so the type library is not polluted with irrelevant properties. Using IntelliSense on an object with only important properties, events, and methods is much easier.
      Since the Session class uses a private datasession, the environment settings that were previously scoped to the datasession had to be set inside the Session class before any code ran. There are a few settings whose default values just don't make sense in a COM server, so developers had to write the code to set them in every single COM server. The Session class in Visual FoxPro 7.0 changes these default values so the developer isn't forced to write the code himself in every object. The values for EXCLUSIVE, TALK, and SAFETY are now OFF by default.

Returning Arrays from Methods

      Also new in Visual FoxPro 7.0 is the ability to return an array from a method or function. You need this if your class implements an interface that requires one of its methods to return an array, but it can be useful for everyday class design as well. To return an array from a method in Visual FoxPro 7.0, simply make sure that the array stays in scope after the method runs (this means either declaring the array as PUBLIC or, preferably, creating an array property of your object), and pass back the array with the @ operator:

  DEFINE CLASS Test AS Session OLEPUBLIC
  
DIMENSION MyArray[2]
FUNCTION ReturnArray(ln1 AS Integer, ln2 AS Integer) AS array
THIS.MyArray [1] = 1
THIS.MyArray [2] = 2
THIS.MyArray [3] = 3
RETURN @THIS.MyArray
ENDFUNC
ENDDEFINE

 

Returning an array also works in Visual FoxPro functions and procedures, but in this case, the only way to make the array stay in scope is to declare it public.

AMEMBERS and COM Objects

      The AMEMBERS function is a good way to explore an object's properties and methods in code. It's invaluable for tools like object browsers or automatic documentation tools that need to get a list of an object's members. The AMEMBERS function creates an array that contains information about that object's methods and properties. Previously, AMEMBERS worked only with native Visual FoxPro objects, and offered no help with COM servers. Visual FoxPro 7.0 now has support for COM objects to AMEMBERS with the addition of a couple of new parameters. Besides simply listing names of parameters, AMEMBERS now optionally creates an array that holds the property type, the function signature, and the Help String for the item.

Advanced COM Features

      The COM features I've covered to this point will be used far and wide by object builders. Developers who use Visual FoxPro have been asking for some of these features for quite some time now. But there are a few more new features that will most likely only be used by the most advanced COM developers. The functions that I'll describe shortly give the developer a degree of control not usually found in a high-level tool like Visual FoxPro, but are critical for certain specific scenarios. With the exception of the COMPROP function (that I mentioned previously), these advanced features are all SYS functions.
SYS(2334)—automation server invocation mode
The return value states whether the object in use was invoked with early binding or late binding. This can be useful when testing client-side code that attempts to acquire an early-bound reference to your COM server.
SYS(2336)—critical sections support
A multithreaded COM server can potentially have two separate threads that each need exclusive access to a shared resource. Since the operating system normally determines when to allocate time to which threads, it's possible that one thread can attempt to access a resource at the same time that another thread is currently using it. To prevent this from happening, you can have a portion of your code use a critical section, preventing other threads from executing the same code at the same time. You can call SYS(2336, 1) to enter a critical section, and SYS(2336, 2) to leave it. Critical sections can be nested, so calling SYS(2336, 3) leaves a critical section, no matter how many times the section was entered prior to that call.
SYS(2340)—Windows NT Service support
When a user logs off a Windows NT® or Windows 2000-based system, the operating system sends every running application a message notifying the application of the event. Visual FoxPro-based applications normally shut down when they receive this message. Since a Windows NT service can run even when there is no user currently logged on, it's critical that an application running as a service should ignore these messages. Calling SYS(2340, 1) enables support for Windows NT Services by causing the server to ignore the WM_QUERYENDSESSION and WM_ENDSESSION sent by Windows when the user logs off.

IDispatch Functions

      The four functions in Figure 10 add support for acquiring pointers to the IDispatch interface of a given object. Visual FoxPro normally hides pointers to objects from the programmer, and only allows you to interact with the object through its reference. Normally this is a really good thing, but occasionally there are situations that require an IDispatch pointer instead of the reference. For instance, AccessibleChildren, a key function in the Accessibility API, takes an IDispatch pointer and returns a structure that contains IDispatch pointers to child objects. When calling AccessibleChildren and processing its results, you'll need to get an IDispatch when given an object, and get an object reference when given an IDispatch pointer. The new IDispatch functions listed in Figure 10 allow you to do this.
      If you're looking for a great way to create a memory leak in your application, use SYS(3097) to increment an object's reference count and then release your reference to that object without decrementing the reference count.

COM Server Properties

      COMPROP is a strange but useful function that allows changing some of the default behavior of COM server properties after instantiation. You can use COMPROP to specify two different types of behavior: Unicode translation and object assignment by reference. The following code tells Visual FoxPro to prevent the value of that property from being translated from Unicode to UTF8 when it's assigned to a local variable.

  =COMPROP(loObject, 'UTF8',1) 
  

 

      In contrast, this code specifies that Visual FoxPro should attempt to assign values to properties of this object by reference instead of by value, and only assign by value if by reference fails.

  =COMPROP(loObject, 'PUTREF',1)
  

 

      The new COM features come together to make Visual FoxPro an even better tool with which to build components, but the new XML functions and support for Web Services come together to really highlight Visual FoxPro as a great choice for component development.

New XML Features

      In the new world of distributed and Web-based applications, the vehicle best suited for carrying data is XML. Since the basic data container in Visual FoxPro is the cursor, writing an n-tier application in Visual FoxPro often requires converting a cursor into XML to get it from one tier to another. While tools have been written to do this, the Visual FoxPro 7.0 CursorToXML function turns out to be faster and more flexible than anything previously available.

  CursorToXML(nWorkArea | cTableAlias, cOutput [, 
  
nOutputFormat [, nFlags [, nRecords [,
cSchemaName [, cSchemaLocation [, cNameSpace ]]]]]])

 

      As you can tell by the number of parameters, there are lots of options for the type of XML to create and the ways to create it. The first parameter obviously specifies the cursor to convert, but from there it gets a little trickier. The second parameter, cOutput, specifies where the results of the function should go, and can be either a file name or the name of a variable that the function will create and place the results in. The third parameter specifies what the second parameter is. The fourth parameter, nFlags, is where much of the flexibility of the function comes in.
      The nFlags parameter can consist of any combination of the values shown in Figure 11. The values are self-explanatory except for bits 4 and 5 (16 and 32), which specify output encoding options. Setting bit 4 (+16) causes the output to use the code page setting of the cursor that's getting converted. Setting bit 5 (+32) causes the encoding attribute of the output to use UTF-8. Setting both bits (+48) sets the output encoding attribute to UTF-8.
      The CDATA option (+8) should be used for cursors with memo fields to ensure that the parser doesn't evaluate character data in that field. Memo fields that contain HTML or reserved characters, for example, would most likely cause the parser to fail when loading the document unless they are wrapped in CDATA sections.
      The fifth parameter of CursorToXML allows you to specify the number of records to write out to the XML document. (If you want to limit which fields are used, you can use SET FIELDS TO prior to issuing a call to the CursorToXML function.)
      The last three parameters allow for different schema options. CursorToXML can create a schema for you and place it inline in the document, or it can create a separate schema file. If you already have a schema, you can specify its location in cSchemaLocation, and CursorToXML can either place the contents of the Schema in the generated document or simply create a reference to it for the parsing application to resolve.
      Of course, most of these parameters are optional, and you'll probably rarely find yourself using all of them. The most common usage will most likely be converting a cursor to XML in methods of a data access layer to serve to business objects or to your presentation layer. For example, a data access object for the TasTrade sample that ships with Visual FoxPro might have a method to return the names and addresses of all the companies that start with any given letters (see Figure 12). Calling the CustomerGetByName function and passing in "Bo" returns the results in Figure 13. There were two records in the resultset.
      The inverse of CursorToXML is XMLToCursor. XMLToCursor takes an XML string and an alias, and creates a cursor:

  XMLTOCURSOR(XMLSource eExpression|cXMLFile [, cCursorName [, nFlags ]])
  

 

Its interface isn't nearly as complicated as CursorToXML, and XMLToCursor is fairly flexible as well. It can deal with a wide array of XML formats (even a persisted ADO recordset) and create a cursor, even in the absence of a schema. The third parameter in XMLToCursor is similar to the fourth in CursorToXML, and has the options shown in Figure 14.
      To show how the combination of CursorToXML and XMLToCursor can help you with n-tier development, suppose that the data access object defined in Figure 12 were compiled into a COM object. A client process using that object might use XMLToCursor like this:

  oDataAccess = CREATEOBJECT("TasTrade.TasTradeDA")
  
* Retrieve all the companies whose name begins with "Bo"
lcXML = oDataAccess.CustomerGetByName("Bo")
=XMLToCursor(lcXML, "Customer")

 

The client process now has a cursor to work with, even though the query ran in the COM server. It shouldn't take you long to figure out that this method of building n-tier apps would be far easier than any hand-coded conversion technique, or even one that uses property-loaded objects to get data across tiers.

Support for Updategrams

      In an effort to make it easier to use Visual FoxPro to program against SQL Server™ data, Microsoft has provided the XMLUpdategram function. Updategrams are XML documents that conform to the updategram spec that SQL Server 2000 can use to perform updates against a database (see https://msdn.microsoft.com/library/en-us/dnsql2k/html/updategrams.asp). SQL Server support for updategrams comes in the form of an ISAPI filter that listens on an Internet Information Services (IIS) virtual directory for posted XML documents. On receipt, the filter processes the updategram, performing the series of inserts, updates, and deletes specified in the document. The updategram spec is rather complex and is outside the scope of the article, so I won't discuss it here except to say that an updategram generated by the Visual FoxPro XMLUpdategram can be submitted to a SQL Server database through IIS or through the ADO Command object to perform updates.
      To show the output that XMLUpdategram generates, make changes to any cursor (after ensuring that table buffering is on and SET MULTILOCKS is on), and issue the XMLUpdategram command (see Figure 15). The code in Figure 16 displays the output. The XML resulting from the XMLUpdategram function can be passed using XMLHTTP to a Web server that is set up to receive Updategrams.

Custom XML Functions

      If you don't like the implementation of any of the new XML functions in Visual FoxPro, that's OK; you can replace them with your own. All you have to do is write an object that implements the IVFPXML interface, compile it as a COM server, and specify that object's ProgID in the new property of the _VFP application object. From then on, any calls to these functions will instead call your custom object. This is a truly interesting little feature Microsoft decided to throw in, and it's a step in a direction which I hope will continue. Getting started with this is easy—just open VFP7.exe in the Object Browser, then drag the IVisual FoxProXML interface to a code editing window to get your shell code.

Web Services

      Microsoft has upheld their commitment to developing the new services-driven Web, with the inclusion of direct support for Web Services in Visual FoxPro 7.0. A Web Service is a business application functionality made available over the Web using HTTP and XML. Microsoft suggests implementation of Web Services using the Simple Object Access Protocol (SOAP). Visual FoxPro 7.0 ships with version 2.0 of the Microsoft SOAP Toolkit, and has hooks that automate the building and calling of Web Services right from the integrated development environment.
      SOAP is simply a description of an XML message format that can be used to invoke methods on objects over the Web. Objects exposed via SOAP can be implemented on any server platform or operating system, and can be called from any client that's connected to the Web.
      The SOAP Toolkit helps you build a SOAP Web Service by generating a Web Services Description Language (WSDL) file that describes the interface of the object and creates a listener that knows how to map the elements of the SOAP message to the methods and parameters of the COM object that will actually handle the request. Visual FoxPro can automate the generation of these documents through the new Web Services builder.

Figure 17 Web Service Location
Figure 17 Web Service Location

      After compiling a COM object from Visual FoxPro, you can invoke the Web Services builder by right-clicking on your project, selecting Builder, then selecting Web Services Publisher from the Wizard Selection dialog. The first time you run the wizard for your project, you will see the dialog in Figure 17, where you'll specify the IIS virtual directory to publish the Web Service to, or specify a new virtual directory for Visual FoxPro to create.

Figure 18 Select COM Component
Figure 18 Select COM Component

      After you have specified where to publish the Web Service, the next dialog (see Figure 18) allows you to select a class from the project that will be published. If you want to change any of the default settings after selecting your class, you should click the Advanced button to set specific options for creating this service. The Web Services Advanced Options dialog (see Figure 19) allows you to change the path of the WSDL and WSML files, and to specify whether your service should use an ASP listener or an ISAPI listener.

Figure 19 Advanced Options
Figure 19 Advanced Options

      Perhaps the most interesting option on this page is the checkbox (on by default) that tells Visual FoxPro to generate IntelliSense scripts from your Web Service. (I'll explain exactly what this does shortly.) You can also specify that this Web Service should be regenerated every time the application is compiled. Visual FoxPro accomplishes this by attaching a special project hook class to your project that knows how to rebuild the Web Service when the project is recompiled.
      After selecting your advanced options, close the dialog and click Generate on the Publish Web Service dialog (as shown in Figure 18) to generate your Web Service files. After the files are generated, you should get a confirmation dialog (see Figure 20) that tells which files were generated.

Figure 20 Confirmation
Figure 20 Confirmation

      Note that the SOAP Toolkit can be used to create Web Services from Visual FoxPro 6.0 COM objects, but since Visual FoxPro 6.0 type libraries don't have strongly typed parameters and return types, the client can't return typed results.
      Your Web Service is now ready to use. To test it, create a new program file and type the following:

  LOCAL oWebService AS 
  

 

      IntelliSense will display a list of valid types for variable declarations (see Figure 21). The name of your Web Service as it appeared in the Advanced Options dialog of the Web Services Publishing Wizard should be in the list of available types. Select your type from the list and take a look at the code that IntelliSense generates for you (see Figure 22). The IntelliSense script that ran when you declared your variable to be a Web Service automatically generated everything after the LOCAL declaration and before the CustomerGetByName method call. The code inserted creates an instance of a Fox Foundation webservices class that wraps the MSSOAP.SOAPClient object and takes care of details like retrying connections and handling exceptions.

Figure 21 Valid Types for Variables
Figure 21 Valid Types for Variables

      Note also that when writing code against the object, the list of available methods appears when you type the period after the object name, and after you select a method, you get a list of parameters and types, demonstrating the full power of extensible IntelliSense in Visual FoxPro.
      You can also have Visual FoxPro generate IntelliSense scripts for Web services built by other parties that reside on other machines across the Web. To program against a Web service on a remote machine, open the IntelliSense Manager dialog from the Tools menu. On the Types tab, click the Web Services button to get to the Register Web Service dialog. In the Web Service Name field, enter the name of the Web Service that you will use when invoking the service via IntelliSense, and the URL of the WSDL file that describes the interface of the Web Service. In the example in Figure 23, I entered the URL for the WSDL that describes a Whois Internet name database service published by www.soapclient.com, and named it Whois.

Figure 23 Registering a Web Service
Figure 23 Registering a Web Service

      After entering this information and clicking Register, the Whois service will be available via IntelliSense by typing the following:

  LOCAL oWhois AS Whois
  

 

Doing so will generate code similar to the code generated for my custom Web Service that you saw in Figure 22. You can write a simple program to test the translator service just by adding a couple of lines to the generated code:

  * Generated code below
  

LOCAL oWhois AS Whois
LOCAL loWS
loWS = NEWOBJECT("Wsclient",HOME()+"ffc\_webservices.vcx")
loWS.cWSName = "Whois"
oWhois = loWS.SetupClient("https://www.soapclient.com/xml/
SQLDataSoap.WSDL",;
"SQLDataSoap", "SQLDataSoapPortType")

* Custom code below

lcDomain = INPUTBOX("Enter a domain to get the Whois information.")
=MESSAGEBOX(oWhois.ProcessSRL("Whois.SRI","whois",lcDomain))

 

      This simple program asks the user for a domain name he wants to fetch the Whois data for, sends the input to the SOAPClient.com Whois service, and displays the results in a messagebox. The few lines of code in this example actually perform a series of fairly complex tasks. They construct a formatted message from the user's input, submit that message to a remote URL, wait for the results, then parse the resulting message to get the return value. This also includes code that ran on the server that parses the request, performs the actual whois lookup, and constructs and returns the XML results.

Creating a Distributed App

      The control over the type library available in Visual FoxPro makes Visual FoxPro-generated COM servers great candidates for Web Services. But combined with the new XML conversion functions, the pieces really come together to make a distributed application easy to write. If your application is designed with n-tier principles in mind and has a data access layer that serves up cursors in the form of XML documents, you can expose those data access objects as secure Web Services to make a desktop application that can run anywhere. Imagine a desktop Customer Relations Management package that accesses a central database on the LAN via DCOM, but can be deployed to run remotely by accessing those same objects over the Internet using SOAP. An application constructed like this knows no physical boundaries. The database is available from anywhere in the world that has an Internet connection. The combination of CursorToXML and XMLToCursor over a Web Service makes it easy to build applications like this.

Conclusion

      For some time now, Visual FoxPro has been a great choice for building desktop database, client/server, and Web applications. The latest edition really solidifies its position as an optimal development tool for these kinds of apps. You might get the impression by reading this article that COM and XML were the main focus of the new version. While there were a large number of improvements in these areas, version 7.0 could also be said to be a developer's release. There are loads of improvements to the IDE, including perhaps the industry's best IntelliSense, dockable windows, an Object Browser, and tons of new functions including some that ease text parsing and string construction. All of these new features add up to what I consider to be the most significant release of Visual FoxPro since version 3.0.

For related articles see:
Walkthrough: Creating Web Services with Visual FoxPro
Visual FoxPro product page
Microsoft XML Developer Center
Erik Moore is an independent consultant specializing in the design and construction of database and Web software. Erik is a Microsoft Most Valued Professional for Visual FoxPro. He can be reached at erik@ekraft.com.


From the October 2001 issue of MSDN Magazine.