Category: Programming FoxPro

  • Visual FoxPro One-To-Many Form

    Visual FoxPro One-To-Many Form

    User Interface Creation without Programming

    Visual FoxPro provides powerful controls that you add with the Form Designer which gives you interfaces that require little or no coding. For example, having the grid control linked to one of your tables makes developing one-to-many forms easy. Just drag a table onto a form to create the grid control. For consistency with other applications you can also create tabbed dialog boxes or your own builder interfaces with the page frame control.

    Creating a One-to-Many Form

    Visual FoxPro makes it easy to set up forms to display records from related tables. Using the Project Manager Window and the Form Designer you can just “drag and drop” the fields you need to set up a form.

    One-to-many forms display data from related tables.

    Try setting up a one-to-many form using two related tables, such as the Customers and Orders tables from Testdata.dbc.

    After four steps, one-to-many forms are…

    …ready to run.

    Specify Tables and Views for Your Form in the Data Environment   You can use the data environment for your form or a report to specify the tables or views used by your form. After you have added the tables and views that have the records you want to display in your form, you can drag the fields to the Form Designer.

    Create Grids by Selecting Multiple Fields   You can select multiple fields and drag them to a form to create a grid object. If you select multiple fields and drag them with the right mouse button, you can choose between a grid or multiple controls. You can also click the Fields item in a table and drag it onto a form to create a grid incorporating all fields in the table. If you right-click a single field and drag it onto a form, you can create a different object class than the one that is assigned as the default object class.

    Giving Your Form a Professional Look

    Be creative and customize your forms with layout, colors, shapes, and graphics.

    Align Controls Exactly   Use the Layout toolbar to quickly line up controls and space them evenly on the form.

    Change the Background Color   Use the Color Palette toolbar.

    Add Graphics and Pictures   Use the Line, Shape, and Image buttons on the Form Controls toolbar.

    Control Display and Data Entry in a Combo Box   You can set the Format Property and InputMask Property for a Combo Box control.

    Remove a Border on a Combo Box or Spinner   You can use the BorderStyle Property to remove a border from the control. For example, if you are using combo box or spinner controls in a grid, you can clean up the display by removing the extra lines the border adds to the grid.

    Use Current Windows Colors for Form Colors   You can set an additional option on the ColorSource Property that lets you set the form colors based on the current Windows color scheme.

    Setting the Properties on Controls

    You can enhance your user interface with the control properties in Visual FoxPro. Using the Properties Window you can see and set the properties associated with each object in your form to determine how a form control looks and behaves.

    Use Data Binding with ActiveX Controls   To bind data to ActiveX controls you can use the ControlSource Property (if the control supports it) in the Properties window to specify a field or variable to which the control is bound.

    Control Navigation in Page Frames   By setting the tab order on your page frame you can control the order that pages are displayed. This also adds the ability to navigate to the different pages in a page frame with the right and left arrow keys.

    Control Item Selection in List Box and Combo Box Controls   To collect the actual value of the selected item in the control, you can set the BoundTo Property to True. To collect the item’s list index number, set the BoundTo property to False.

    Select More Than 60 Items in a List Box   You can now select an unlimited number of multiple items in a ListBox Control.

    Create Hidden Properties and Methods   You can set accessibility properties on the properties and methods you create to Public, Protected, or Hidden. To change the accessibility of properties and methods, use the Members Tab, Class Info Dialog Box.

    For more information aboutSee
    Creating formsCreating Forms
    Using form wizardsHelp by pressing F1 from the wizard.
    Using buildersHelp by pressing F1 from the builder.
    Using controlsHelp by searching on the control name or Using Controls
    Using forms in applicationsCreating Forms

    Customizing Your Form Designer

    To make your work easier, you can customize the Form Designer to match your work style and needs.

    Save Changes Automatically   You can set options so Visual FoxPro saves your forms automatically when you quit the Form Designer. In the Forms Tab, Options Dialog Box you can choose the option to Save Changes Before Running Form. If you select this option, you skip the confirmation dialog box, and the changes you made to the current form are saved automatically.

    Change the Font in the Properties Window   You can choose one of three font settings from the Properties window shortcut menu.

    Use the Keyboard in the Property Window   The Properties window supports keyboard navigation for scrolling through the hierarchy of objects, controls, and tab orders. Use CTRL+PGUP and CTRL+PGDN to move up and down through the list.

    For more information aboutSee
    The Form Designer and form controlsForm Designer
    Creating formsCreating Forms

    Add Controls from a Class Library

    When you need to add controls to a form so you can navigate through the records in it, you can choose from a library of controls, drag in the controls you need, and run the form. It’s that simple.

    Adding VCR Controls to a One-to-Many Form

    You can use the following example to see how easy it is to streamline your work using controls from class libraries.

    Store complicated control sets in a class library for reuse.

    Add VCR Controls to a Form   By opening the sample button class library (Buttons.vcx) supplied with Visual FoxPro, you can easily add a set of VCR-style navigation control buttons to your form. The sample libraries are in the Visual FoxPro …\Samples\Classes directory.

    A class for VCR controls can be added to your toolbar.

    Controls are easy to add to your form.

    Set the SkipTable Property   Because this is a one-to-many form, you need to specify that the next record in the main table is selected when you choose the Next button. To do this, open the Properties Window and set the SkipTable property so that it refers to the main table.

    The SkipTable property adds the final functionality to your control.

    Expanding Your Class Libraries

    You don’t have to wait for someone else to sell you their control classes and libraries; you can make your own.

    Create Your Own Classes   You can create your own controls and save them in class libraries. You can start with standard Visual FoxPro classes and customize them to create your own version, or you can create your own classes.

    Point to Classes in Compiled Applications   You can use the SET CLASSLIB Command to point to a class included in a compiled application.

    Add and Keep Classes on the Forms Control Toolbar   When you add a class to the Forms Control toolbar, the class remains on the toolbar until you click the View Classes button again and select Standard.

    View Detailed Information about Your Classes   Using the Class Browser Window, you can view the entire hierarchy of classes and objects in a class library or form.

    Create Your Own Subclasses of ActiveX Controls   You can create subclasses of all the ActiveX controls shipped with Visual FoxPro.

    For more information aboutSee
    Using the Class DesignerHelp by pressing F1 from the Class Designer.
    Setting propertiesCreating Forms
    Creating classes and using class librariesObject-Oriented Programming

    Creating a Menu Bar and Shortcut Menus

    You can use the Menu and Shortcut Designers to add a menu bar and menus to forms in your application. With the Menu and Shortcut Designers you can create shortcut menus and menus for SDI forms.

    When you create a menu, you have the option of adding a menu to the menu bar or creating a shortcut menu. The Shortcut Designer presents the familiar Menu Designer interface, but allows you to design shortcut menus rather than menu bars and menus.

    After you have generated your shortcut menu, you can attach it to any control by running the menu in the control’s RightClick Event.

    Creating Reports

    When it’s time to print out important data, you don’t want to spend a lot of time formatting reports. That’s why Visual FoxPro provides report wizards to streamline the reporting process. With report wizards you can select the data you want from your tables and views, and present it in your choice of professional looking formats.

    It’s easy to combine information from two different tables in a one-to-many report.

    Using the Visual FoxPro report design features, you can:

    Use the Preview Button   The last screen of a report wizard has a Preview button so you can quickly see if your report is the way you like it. If not, use the Back button in the wizard and make the necessary adjustments.

    Use Quick Report for Fast Results   If you’re setting up your own report in the Report Designer, choose Quick Report from the Report menu to add selected fields from a table to a report.

    Use Query or View Results in a Report   Want to print out the results of a query? Choose the Report option in the Query Destination Dialog Box. You can send the query results to a pre-formatted report or send the query to a report wizard.

    Create a One-To-Many Report   The One-To-Many Report Wizard makes it easy to set up a report that shows fields from two tables joined in a one-to-many relationship. For example, try using the wizard to create a report from two related tables in Testdata.dbc.

    Report Wizards retrieve records and format the report layout.

    Use Other Report Wizards   Try using the other report wizards. For simple one-table reports or summary reports with totals, choose Report Wizard.

    Use the Report Designer   If you want to customize your wizard-generated report further, just open it in the Report Designer and add the enhancements you need.

    For more information aboutSee
    Report WizardsHelp by pressing F1 from the wizard.
    Report DesignerAdding Reports and Labels

    Providing a Help System

    You can add value to your application by creating a Help system. Using the Help compiler included with Visual FoxPro, you can turn your document files into a graphical Help system to distribute with your applications. If you prefer to use a Visual FoxPro table to store your help information, you can create .DBF-style help. You can find a sample, Ttrade.dbf, in the …\Samples\Taztrade\Help directory. For more information about creating WinHelp and HTML Help, see Creating Graphical Help or Creating .DBF-Style Help.

    See Also
    Overview of Visual FoxPro Features | Application Management with the Project Manager | Rapid Application Creation | Application Customization with Designers | Form Designer. | Creating Forms

    Rapid Application Creation

    You can develop applications rapidly with help from Visual FoxPro wizards, builders, toolbars, and designers. In addition, with the Visual FoxPro object and event model, you can create prototypes and implement applications quickly.

    Getting Quick Results with Wizards and Builders

    Using wizards and builders, you can have any or all of the components of your application completed in a matter of minutes. In addition, if any component you create with a wizard or builder does not fully meet your needs, you can alter the component using a designer.

    Creating an Application Now

    For quick results, wizards are the answer. They provide step-by-step prompts for common tasks you perform in Visual FoxPro. For example, the Table Wizard guides you through the process of creating a table, the Form Wizard creates a form, and the Web Publishing Wizard helps you create a Web page that searches your tables.

    Take a look at what you can do:

    • Develop Instant Applications   Using the Application Wizard, you can quickly create an application by using your tables and forms, or by using a sample set of application components.
    • Create an Internet Web Page   You can create a Web page that provides Web surfers with search and retrieval access to your tables.
    • Add Page Frames to Forms   If you choose more fields than can fit on the form, you can add a page frame to display the additional fields on additional pages.
    • Populate Pivot Tables through ODBC   The PivotTable Wizard uses the Visual FoxPro ODBC driver.
    • Show Nulls in a Cross-Tab Report   The Cross-Tab Wizard now supports displaying null values.
    • Move Your Data to a Back-End Server   You can move Visual FoxPro databases, tables, and views from your system to a back-end server with the SQL Server Upsizing Wizard.

    If you are in a hurry, use the table in Wizards Overview to select a wizard, and begin creating your application or component.

    If you do not want to experiment with your own data and application components, use the samples provided with Visual FoxPro. You can work in the sample database Testdata.dbc, located in the …\Samples\Data directory.

    Taking Shortcuts with Toolbars

    Like other Microsoft applications, Visual FoxPro includes toolbars containing buttons that represent common tasks you perform or objects you use frequently. To perform a specific task or use a particular object, choose the appropriate button.

    You can customize the toolbars that come with Visual FoxPro. You can also define custom toolbars for the applications you write. For more information about toolbars, see Customizing the Visual FoxPro Environment or Designing Menus and Toolbars.

    Building Controls Quickly

    Just as wizards can help to create reports and forms, builders are visual tools that help you design controls to your specifications. Builders can save you time when you are setting up many forms or prototyping applications. All you do is start the builder and answer a series of questions. Using your answers, the builder sets properties for the control you are adding.

    Add a Combo Box to Your Form   Suppose you want to enhance a simple form you created by adding the ability to select field values from a list, rather than moving through all the records with VCR buttons and viewing the values in a text box. Just replace the current text box with a combo box and use a builder to set the properties.

    Modify Existing Controls with a Builder   If you want to change an existing control, you can select the control and use a builder to help you set the control’s properties.

    For example, the Combo Box Builder is a tabbed dialog box that helps you design a combo box on a form. A combo box gives you the same view of a value as a text box control, but allows you to choose from a list like a list box control. With this builder you can set properties of a combo box. As you set each property, the control on the form changes to match the settings.

    The Builder Lock button and Combo Box button

    The tabs in the Combo Box Builder dialog box

    Build a Grid Control   To display a small spreadsheet-like view of your data, you can use a grid control. Even if you aren’t familiar with all the properties of a grid control, you can still take advantage of their functionality using a builder. The builder helps you choose the fields for your grid, select a style and layout, and establish relationships between tables bound to the grid control. As you make changes in the builder, they are reflected in the Form Designer.

    Visual FoxPro offers a wide selection of builders.

    ToUse this builder
    Format a group of controlsAutoFormat Builder
    Build a combo boxCombo Box Builder
    Build a command groupCommand Group Builder
    Build an edit boxEdit Box Builder
    Create and edit expressionsExpression Builder
    Build a formForm Builder
    Build a gridGrid Builder
    Build a list boxList Box Builder
    Build an option groupOption Group Builder
    Ensure referential integrityReferential Integrity Builder
    Build a text boxText Box Builder

    The Advantages of More Power

    Visual FoxPro is much more powerful than its predecessors. Object-oriented programming speeds up application development through features such as subclassing. The Visual FoxPro event model eliminates the need to use foundation READ or write event handlers. Optimizing your system settings and implementing Rushmore technology improves the performance of Visual FoxPro.

    Harnessing the Power of Object-Oriented Programming

    Using the Visual FoxPro object model, you can employ all the features of object-oriented programming including inheritance, encapsulation, polymorphism, and subclassing. For details about object-oriented programming with Visual FoxPro, see Object-Oriented Programming.

    You can speed up application development by using classes. For example, you can create a basic form, toolbar, or page frame by using the form, toolbar, or page frame base class provided with Visual FoxPro.

    Then you can reuse your code and forms by subclassing the classes you defined. For example, you can subclass the Form base class to create a custom class that automatically gives all the forms in your application the look and feel you want.

    The Visual FoxPro class model gives you extensive control over the objects in your application. You have as much control of the appearance and behavior of objects on forms at run time as you do in the Form Designer at design time.

    With Visual FoxPro, you can create classes visually with the Class Designer or programmatically with the DEFINE CLASS Command. For more information, see Object-Oriented Programming.

    Handling Events with Ease

    With Visual FoxPro you can create fully event-driven applications. You can access all standard Windows events such as mouse movements that permit dragging or dropping objects. You can control events visually through the Properties window or programmatically through the Visual FoxPro language. For more information about the event model, see Understanding the Event Model.

    Testing and Debugging Immediately

    Using the debug environment you can test and debug your code at the same time you design each component of your application and write the code. The Visual FoxPro language has been enhanced to include support for debugging capabilities, collection properties, checking the lock status of files and the current record, Value tips, and more. Also, the debugging tools work independently from the main application and do not interfere with the interface you are trying to test.

    Test as You Design   The enhanced Visual FoxPro development environment gives you more power to test your component design and code. For example, if you are designing a form, you can click the Run button to run the code. If you want to change something, just click the Design button and make your changes.

    Catch Syntax Errors Early   The editor now features color-coded editing: you can specify colors for syntax, normal text, variables, comments, and more in the Edit Properties Dialog Box. If you mistype a command or keyword, you’ll know right away because it’s the wrong color.

    Debug with Power

    You can debug and monitor your application components more easily in this version of Visual FoxPro using the Debugger Window.

    • View Properties of an Object   In the debugger you can view the elements within a collection or array using the Watch Window to show a hierarchical display of an object’s properties.
    • Trace the Execution Process of Your Code   In the Trace Window you can see each line of code as it executes and check the values of all variables, properties, and environment settings.
    • Control Execution with Breakpoints   Using breakpoints you can control when execution stops using one of four types of breakpoints. You can stop at a particular line, when a certain value changes, if a condition evaluates to true, or if a condition for a certain line is true.
    • Set Asserts and Display Assert Messages   You can use the ASSERT Command to specify conditions that verify that the code is running properly. When an error occurs, the Debug Output Output Window records the message.
    • Check the Current Value of a Variable   To quickly check values you can place the cursor over a variable in the Trace Window to display a Value Tip.
    • Watch and Change Values   In the Watch Window and Locals Window you can change values for properties or variables while you run the code.
    • Display Output from Message Requests   In the Debug Output Window you can conveniently display interactive or coded information requests in a separate window from the active window or screen.
    • Use a Coverage Log File   In the Debugger Window you can use the coverage option to log the execution information about your code such as how long the line took to execute, the line number, and more.

    For more information about testing and debugging your application, see Testing and Debugging Applications.

    Optimizing Your System

    Visual FoxPro brings you improved performance across all these product areas:

    • Engine
    • Form/Object
    • OLE
    • Client/Server
    • Wizards/Builders
    • Language

    You can also improve performance by enhancing startup speed and optimizing SET commands. For more information about optimizing your system, see Optimizing Your System.

    Optimizing Applications with Rushmore Technology

    You can dramatically improve the performance of queries by using Rushmore technology. Rushmore is an exclusive technology for rapidly selecting sets of records from tables. It can reduce query response times from hours or minutes to seconds. For more information about using the Rushmore technology, see Optimizing Applications.

    Interaction with Other Applications

    With Visual FoxPro you can share data with other applications such as Microsoft Excel and Word, use objects from other applications, and control other applications using Automation.

    Sharing Data with Other Applications

    You can take advantage of all the software tools on your desktop with Visual FoxPro.

    Use Your FoxPro Data in Other Applications   Sharing data with other applications is easy – for example, you can share Visual FoxPro data with Microsoft Excel by using the PivotTable Wizard, or with Microsoft Word by using the Mail Merge Wizard.

    Display or Store Objects from Other Applications   You can include objects from another application by linking or embedding them in tables or in forms. Then you can edit the objects directly, without leaving Visual FoxPro.

    Editing included objects is easy in Visual FoxPro.

    Importing and Exporting Data

    You can import and export data between Visual FoxPro and many other applications using a variety of file formats: text, spreadsheets, and tables, to name a few. The data can be appended to or copied from existing tables easily. The Import Wizard helps you make decisions about the structure you want the data to take in Visual FoxPro.

    For more information, see Importing and Exporting Data.

    Integrating with Automation

    With Visual FoxPro you have the power to fully integrate your application with other applications.

    Control Other Applications   You can extend the power of your application by controlling other applications programmatically. For example, you can direct Microsoft Excel to perform some calculations, instruct Microsoft Graph to chart the results, and then store the chart in a General field of a Visual FoxPro table, all from Visual FoxPro code. Or, you can add ActiveX controls that you or someone else developed.

    Optimize Automation Requests   You can determine how much time your system uses for requests and how long it takes for messages to appear for your user. For example, you can control how much time your system spends retrying Automation requests before displaying a “server busy” message.

    You can also control the number of milliseconds that pass during user input, such as using the mouse or keyboard while an Automation request is pending, and a “busy” message appearing.

    Control Visual FoxPro from Other Applications   Using Visual FoxPro as an Automation server, you can create references to Visual FoxPro objects, execute Visual FoxPro commands, and run Visual FoxPro programs from other applications.

    For more information on Automation, see Adding OLE.

    Creating Active Documents

    Active Documents are non-HTML documents viewed in a web browser such as Microsoft Internet Explorer. You can now create Active Document applications in Visual FoxPro. A Visual FoxPro Active Document is an application (.app) created from a Visual FoxPro project whose main file is set to a class based on the ActiveDoc base class.

    Use the Project Manager and the Class Designer to create your Active Doc application. For more information on creating Visual FoxPro Active Documents, see Interoperability and the Internet. See ActiveDoc Object for details on its properties events and methods.

    Client/Server Solution Development

    You can use Visual FoxPro as a front end for developing robust client/server applications. Visual FoxPro combines the higher-level support of updatable views of server data with direct access to native server syntax using SQL pass-through. This gives you a solid foundation on which to build versatile client/server solutions. A full-featured data dictionary, local and remote views, null support, transactions, international application support, and access to any ODBC data source all contribute to the features you need for client/server development.

    Increasing Client/Server Performance

    You can increase the performance of your client/server application using the properties and ODBC driver provided in Visual FoxPro.

    Controlling Performance with Properties

    You can increase performance by using the following cursor and remote view properties:

    • CompareMemo
    • FetchAsNeeded
    • Prepared

    You can display these properties using the DBGETPROP( ) Function and CURSORGETPROP( ) Function functions, or set the properties with the DBSETPROP( ) Function and CURSORSETPROP( ) Function functions.

    Include or Exclude Memo Fields in Update Detection   You can use the CompareMemo property to control when memo fields are used to detect update conflicts. This view and cursor property determines whether memo fields (types M or G) are included in the update WHERE clause. The default setting, True (.T.), means that memo fields are included in the WHERE clause. If you set this property to False (.F.), memo fields don’t participate in the update WHERE clause, regardless of the settings of UpdateType.

    Optimistic conflict detection on Memo fields is disabled when CompareMemo is set to False (.F.). For conflict detection on memo values, CompareMemo should be set to True (.T.).

    Fine Tune Record Fetching   Use the FetchAsNeeded property to specify if all rows are fetched progressively, or only those within the row set determined by the FetchSize property. If you want to fetch all of the rows in the result set using the Visual FoxPro idle loop, use the default setting, False (.F.). If you want to fetch only when the user scrolls beyond the row set determined by the FetchSize property, set FetchAsNeeded to True (.T.) to fetch the next row set.

    Speed Up Re-query Operations on Parameterized Views   Using the Prepared property, you can set whether the query for the view is prepared before it is executed. If you set this property to True (.T.), Visual FoxPro sends a request to the ODBC driver to prepare or compile the SQL query that defines the view. If the ODBC driver for your back-end data source supports the use of prepared statements, subsequent re-queries on the open view are executed faster.

    Using ODBC for Quick Access to Visual FoxPro Data

    The Visual FoxPro ODBC Driver provides quick access to Visual FoxPro data. End users and developers of many applications, including Microsoft Access, Microsoft Excel, Visual C++, and Visual Basic, can use this driver to retrieve, manipulate and update Visual FoxPro data. For more information about the Visual FoxPro ODBC Driver, see the Drvvfp.hlp file.

    Defining Rules with the Data Dictionary

    Visual FoxPro databases (.dbc files) provide a data dictionary that allows you to add rules, views, triggers, persistent relationships, and connections to every table in a database.

    Within a database you can define:

    • Field-level or record-level rules that Visual FoxPro enforces wherever the table is used in an application.
    • Primary and candidate index keys.
    • Local and remote views.
    • Triggers.
    • Persistent relationships between database tables.
    • Connections to remote data sources.
    • Stored procedures.
    • Default values on fields.
    • Long table and field names.

    In addition, you can enforce referential integrity for every persistent relationship by using the Referential Integrity Builder to define rules for inserts, updates, and deletes.

    Visual FoxPro also supports null values in tables, greatly improving compatibility and connectivity with other data sources such as Microsoft Access, Visual Basic, and SQL-based servers. For details about null values, search for “null” and “null values”.

    Each Visual FoxPro database is completely user-extensible, with access through language enhancements and the visual designers. For more information about databases and data dictionaries, see Creating Databases.

    View Local, Remote, and Offline Data   You can design and test a client/server application on your local computer using data from remote, local, or multiple-table heterogeneous views. Local views use tables on your local computer rather than tables on a remote server, and multiple table views use related data from separate tables. You can create parameterized views to minimize the amount of data you download from the server, take the data on the road, or update remote data from your Visual FoxPro application. For more information about views see Creating Views.

    Manage Shared Access with Transactions   You can design your application to provide shared access to data. Shared access involves sharing data among users and restricting access when necessary. Transactions and buffering — pessimistic or optimistic, record-level or table-level — mean less programming for you. Built-in batch processing and detailed control over handling update conflicts simplifies updating data in a multiuser environment. For details about designing your application for shared access, see Programming for Shared Access.

    Develop International Applications   Visual FoxPro provides several areas of support for developing international applications. For example, Visual FoxPro supports several code pages. The code pages for Greek and Russian are supported on MS-DOS, Microsoft Windows, and Macintosh platforms. Visual FoxPro also supports double-byte character sets for languages such as Japanese, Korean, Traditional Chinese, and Simplified Chinese. In addition, Visual FoxPro supports collation sequences for languages such as Japanese, German, Traditional Chinese, and Icelandic. For more information about international applications, see Developing International Applications.

    Implement a Client/Server Application   In addition to using views for client/server development, you can send any native server syntax you want directly to a server with Visual FoxPro SQL pass-through functions. These functions permit additional server access and control beyond the capability of views. For details about SQL pass-through see Implementing a Client/Server Application.

    After designing your application locally, you can upsize and implement it against a back-end data source. Upsizing applies the benefits of client/server architecture to the local application and allows you to create a remote server database with the same table structure and data as the original Visual FoxPro tables. When you upsize, you choose which tables move to the server and which remain locally for more immediate access. For details about upsizing see Upsizing Visual FoxPro Databases.

    Visual FoxPro and XML

    Extensible Markup Language (XML) is the standard language for describing and delivering data on the Web, just as Hypertext Markup Language (HTML) is the standard language for creating and displaying Web pages. XML uses tags and attributes to delimit pieces of data, and leaves the interpretation of the data completely to the application that reads it. For more details, see XML Functions.

    To make it possible for you to more easily implement data exchange via XML, Visual FoxPro provides new functions and functionality in the following functions:

    • CURSORTOXML( )   Converts a Visual FoxPro cursor into XML.
    • XMLUPDATEGRAM( )   Creates an XML UpdateGram from changes made to a buffered table or cursor.
    • XMLTOCURSOR( )   Converts XML data to a Visual FoxPro cursor or table.

    Miscellaneous XBase Features

    Visual FoxPro includes a number of new and improved XBase features, those written in the Visual FoxPro language itself. These include new tools such as the Task List Manager, Object Browser, IntelliSense Manager, Accessibility Browser, Automated Test Harness and Web Services wizard. These are described in more detail in other What’s New topics.

    In addition to the above-mentioned features, Visual FoxPro contains a wealth of other XBase improvements.

    • New Foundation Classes have been added for use with Microsoft Agent, Regular Expressions, Cryptography, Enhanced File Open dialog boxes, Windows 2000 Logo information, and Web Services.
    • The SQL Server Upsizing wizard contains a number of new improvements.
    • New Solution samples have been added to better illustrate some of the new Visual FoxPro features and foundation classes.
    • A new set of COM+ Service samples show how you can use Visual FoxPro servers with COM+ Services such as Transactions, Queued Components, COM+ Events and Compensating Resource Managers. Many of these samples use new Visual FoxPro server enhancements.
    • GENMENU has been updated to support new image support for menus. A new directive (#IMAGEPATHS) has been added to control if full paths are generated for menu images.

    Visual FoxPro OLE DB Provider

    The Visual FoxPro OLE DB Provider makes it possible for application developers to access Visual FoxPro data from other languages and applications, as well as from Visual FoxPro when appropriate. The Visual FoxPro provider exposes OLE DB interfaces to consumers wanting access to data on one or more Visual FoxPro databases. You can use it as part of an OLE DB consumer for Visual FoxPro databases.

    The Visual FoxPro OLE DB Provider supports new Visual FoxPro features, such as database container (DBC) events, access to stored procedures, and the ability to create, modify, and delete functions and procedures in the DBC stored procedure module.

    In the Visual FoxPro OLE DB Provider, you will find an improved threading model for better scalability and all the functionality of the Visual FoxPro ODBC driver of earlier versions of FoxPro.

    Behavior Changes since the Previous Version

    This section describes behavioral characteristics of Visual FoxPro that differ from those of the previous version. It is possible that these changes affect existing code.

    • _SCREEN and _VFP coordinate properties (Top, Left, Height, Width) have been changed to better distinguish between the two windows they represent. The _VFP coordinates represent the entire Visual FoxPro application window (including menu and status bars). The _SCREEN coordinates represent the desktop area (for example, the window to which ? output is directed).
    • For performance reasons, the CreateObject( ) function no longer adds a sequential number to the value for a dynamically created Name property. This applies to class definitions in .PRG files that do not have an explicitly set Name property and base classes.
    • Because of new support for IntelliSense and COM server typing (for example, LOCAL cName AS string), the use of a variables list separated by spaces only (for example, LOCAL x y z) is no longer allowed. You must separate variables with commas (for example, LOCAL x, y, z).
    • The Session class now hides intrinsic properties, methods, and events in the generated type library for an OLEPUBLIC subclass. Additionally, when using a private data session with a Session object, the default setting for SET TALK, SET EXCLUSIVE, SET SAFETY is now OFF.
    • The VERSION( ) format has changed. The Build number is now the last set of digits. This also might affect usage of the AGETFILEVERSION() function in your code.
    • Some of the values of HOME([n]) have changed. Some of these changes only apply to an installation of Visual Studio. The location of the Samples and Graphics folders is now under the Visual FoxPro root directory.
    • The new default location to save files has been changed for compliance with Windows 2000 Logo guidelines. You can view (and change) this location from the Options dialog box (File Locations tab).
    • The format for menus (MNX file) has changed to include new Picture support.
    • The format for a database (DBC) file will be altered if DBC Events is enabled. This will render the DBC incompatible with prior versions of Visual FoxPro and the ODBC driver. You can restore backward compatibility by disabling DBC events for the DBC. The Visual FoxPro OLE DB provider does support DBC Events.
    • The Command window contents are now persisted between sessions of Visual FoxPro. The contents are stored in a file named _command.prg.
    • The resource file (for example, FOXUSER) is now opened shared, so you can run multiple instances of Visual FoxPro using the same FOXUSER file. As in prior versions, you can set the resource file from the Options dialog box.
    • The Visual FoxPro run-time libraries (for example, VFP7T.DLL) are no longer installed in the WinSys directory. Additionally, resource files for other languages are now installed with the English product.
    • The Properties window stays open when it is set as Dockable, even if the current form/class designer is closed.
    • Old FoxBASE+ support for FOXGRAPH, FOXVIEW, FOXGEN, FOXCODE, CENTRAL, and ASSIST is no longer supported.
    • The Windows Explorer shell open functionality for known Visual FoxPro file types now launches a new instance of Visual FoxPro. Additionally, Program and Query files support both Open and Run support. The default for Program files is now Open instead of Run as in previous versions.
    • The Class/Form Designer surface grid now displays in actual grid coordinates instead of the behavior of previous versions of Visual FoxPro, which was to display in 2x actual setting.
    • Grid column headers now allow for display of keyboard shortcuts. This is for display only, so developers must write code to handle the shortcut action.
    • Search path for #INCLUDES has been extended for PRGs, SCXs and VCXs.
    • The format of the event-tracking log generated using the SET EVENTTRACKING command now includes a TimeStamp column.
    • The _DBLCLICK system variable no longer controls incremental search duration for controls such as list boxes. Now this is handled by a new system variable called _INCSEEK.
    • A startup application (for example, VFP7Strt.app) no longer ships with Visual FoxPro.
    • The nStartPos parameter for ASCAN() will only produce an error if a value of 0 is passed. Values passed which are larger than the length of the array will return 0.
    • In previous versions, CREATE TABLE ... NAME LongTableName substituted spaces with underscores (“_”) for the LongTableName when called programmatically. This would not occur when specified in the Table Designer. In this version, spaces are now preserved, which is now consistent with the behavior of the Table Designer.

    Feature Changes since the Previous Version of Visual FoxPro

    The following features from the previous version of Visual FoxPro have been removed from the product:

    • The Spell Checker is no longer included with the product.
    • The Setup wizard is no longer included with the product. A Microsoft Installer based deployment tool will be available. For details, see Distributing Applications.
    • The Graph runtime is no longer included, because it was primarily for distribution with the Setup wizard.
    • The Run ActiveDoc menu item is no longer in the product, but the program is still available and can be called from the Do menu.
    • Several of the MSDN menu items have been removed from the Help menu. Online documentation for the product is available in a stand-alone help file as well as in the MSDN Library. Additionally, the Microsoft on the Web submenu has been replaced with a single Visual FoxPro on the Web menu item.
    • The Calendar, Outline, and FoxHWnd controls no longer ship with Visual FoxPro.
    • The Visual FoxPro ODBC Driver no longer ships with Visual FoxPro. This is available from the Visual FoxPro Web site (http://msdn.microsoft.com/vfoxpro/). The Visual FoxPro OLE DB Provider is the preferred solution for clients accessing Visual FoxPro data remotely.

    Classes and Objects: The Building Blocks of Applications

    Classes and objects are closely related, but they are not the same. A class contains information about how an object should look and behave. A class is the blueprint or schematic of an object. The electrical schematic and design layout of a telephone, for example, would approximate a class. The object, or an instance of the class, would be a telephone.

    Objects Have Properties

    An object has certain properties, or attributes. For example, a phone is a certain color and size. When you put a phone in your office, it has a certain position on your desk. The receiver can be on or off the hook.

    Objects you create in Visual FoxPro also have properties that are determined by the class the object is based on. These properties can be set at design time or at run time.

    For example, some of the properties that a check box can have are listed in the following table:

    PropertyDescription
    CaptionThe descriptive text beside the check box.
    EnabledSpecifies whether a user can choose the check box.
    ForeColorThe color of the caption text.
    LeftThe position of the left side of the check box.
    MousePointerHow the mouse pointer looks when over the check box.
    TopThe position of the top of the check box.
    VisibleSpecifies whether the check box is visible.

    Objects Have Associated Events and Methods

    Each object recognizes and can respond to certain actions called events. An event is a specific and predetermined activity, initiated by either a user or the system. Events, in most cases, are generated by user interaction. For example, with a phone, an event is triggered when a user takes the receiver off the hook. Events are also triggered when the user presses the buttons to make a call.

    In Visual FoxPro, user actions that trigger events include clicks, mouse moves, and key presses. Initializing an object and encountering a line of code that causes an error are system-initiated events.

    Methods are procedures that are associated with an object. Methods are different from normal Visual FoxPro procedures: methods are inextricably bound with an object and are called differently than normal Visual FoxPro procedures are called.

    Events can have methods associated with them. For example, if you write method code for the Click event, that code is executed when the Click event occurs. Methods can also exist independently of any events. These methods must be explicitly called in code.

    The event set, while extensive, is fixed. You can’t create new events. The method set, however, is infinitely extendible.

    The following table lists some of the events associated with a check box:

    EventDescription
    ClickUser clicks the check box.
    GotFocusUser selects the check box by clicking it or tabbing to it.
    LostFocusUser selects another control.

    The following table lists some of the methods associated with a check box:

    MethodDescription
    RefreshThe value of the check box is updated to reflect any changes that may have occurred to the underlying data source.
    SetFocusThe focus is set to the check box just as though the user had pressed the TAB key until the check box was selected.

    Classes in Visual FoxPro

    All of the properties, events, and methods for an object are specified in the class definition. In addition, classes have the following characteristics that make them especially useful for creating reusable, easily maintained code:

    • Encapsulation
    • Subclasses
    • Inheritance

    Hiding Unnecessary Complexity

    When you include a phone in your office, you probably don’t care how the phone internally receives a call, initiates or terminates connections to electronic switchboards, or translates key presses into electronic signals. All you need to know is that you can lift the receiver, dial the appropriate numbers, and talk to the person you want to talk to. The complexity of making that connection is hidden. The benefit of being able to ignore the inner details of an object so you can focus on the aspects of the object you need to use is called abstraction.

    Encapsulation, which involves packaging method and property code together in an object, contributes to abstraction. For example, the properties that determine the items in a list box and the code that executes when you choose an item in the list can be encapsulated in a single control that you add to a form.

    Leveraging the Power of Existing Classes

    A subclass can have all the functionality of an existing class, plus any additional controls or functionality you want to give it. If your class is a basic telephone, you can have subclasses that have all the functionality of the original telephone and any specialized features you want to give them.

    Subclassing is one way to decrease the amount of code you have to write. Start with the definition of an object that is close to what you want, and customize it.

    Streamlining Code Maintenance

    With inheritance, if you make a change to a class, that change is reflected in all subclasses based on the class. This automatic update saves you time and effort. For example, if a phone manufacturer wanted to change from dial to push-button style phones, it would save a lot of work to be able to make the change to the master schematic and have all previously manufactured phones based on that master schematic automatically inherit this new feature, rather than having to add the new feature to all the existing phones individually.

    Inheritance doesn’t work with hardware, but you do have this capability in software. If you discover a bug in your class, instead of having to go to each subclass and change the code, you fix it once in the class and the change propagates throughout all subclasses of the class.

    Containers and Non-Containers

    The two primary types of Visual FoxPro classes, and by extension Visual FoxPro objects, are container classes and control classes.

    Container Classes

    Containers can contain other objects and allow access to the objects contained within them. For example, if you create a container class that consists of two list boxes and two command buttons, and then add an object based on this class to a form, each individual object can be manipulated at run time and design time. You can easily change the positions of the list boxes or the captions of the command buttons. You can also add objects to the control at design time; for example, you can add labels to identify the list boxes.

    The following table lists what each container class can contain:

    ContainerCan contain
    Command button groupsCommand buttons
    ContainerAny controls
    ControlAny controls
    CustomAny controls, page frame, container, custom
    Form setsForms, toolbars
    FormsPage frames, any controls, containers, custom
    Grid columnsHeaders and any objects except form sets, forms, toolbars, timers, and other columns
    GridsGrid columns
    Option button groupsOption buttons
    Page framesPages
    PagesAny controls, containers, custom
    ProjectFiles, servers
    ToolbarsAny controls, page frame, container

    Control Classes

    Control classes are more completely encapsulated than container classes are, but can be less flexible for that reason. Control classes do not have an AddObject method.

    Preparation for Class Creation

    You want to be able to use classes in many different contexts. Smart planning will enable you to most effectively decide what classes to design and what functionality to include in the class.

    Deciding When to Create Classes

    You could create a class for every control and every form you might ever use, but this isn’t the most effective way to design your applications. You’ll likely end up with multiple classes that do much the same thing but must be maintained separately.

    Encapsulate Generic Functionality

    You can create a control class for generic functionality. For example, command buttons that allow a user to move the record pointer in a table, a button to close a form, and a help button, can all be saved as classes and added to forms any time you want the forms to have this functionality.

    You can expose properties and methods on a class so that the user can integrate them into the particular data environment of a form or form set.

    Provide a Consistent Application Look and Feel

    You can create form set, form, and control classes with a distinctive appearance so that all the components of your application have the same look. For example, you could add graphics and specific color patterns to a form class and use that as a template for all forms you create. You could create a text box class with a distinctive appearance, such as a shadowed effect, and use this class throughout your application any time you want to add a text box.

    Deciding What Type of Class to Create

    Visual FoxPro allows you to create several different kinds of classes, each with its own characteristics. You specify the type of class you want to create in the New Class dialog box or in the AS clause in the CREATE CLASS command.

    The Visual FoxPro Base Classes

    You can create subclasses of most of the Visual FoxPro base classes in the Class Designer.

    Visual FoxPro Base Classes

    ActiveDocCustomLabelPageFrame
    CheckBoxEditBoxLineProjectHook
    Column*FormListBoxSeparator
    CommandButtonFormSetOLEBoundControlShape
    CommandGroupGridOLEContainerControlSpinner
    ComboBoxHeader*OptionButton*TextBox
    ContainerHyperlink ObjectOptionGroupTimer
    ControlImagePage*ToolBar

    * These classes are an integral part of a parent container and cannot be subclassed in the Class Designer.

    All Visual FoxPro base classes recognize the following minimum set of events:

    EventDescription
    InitOccurs when the object is created.
    DestroyOccurs when the object is released from memory.
    ErrorOccurs whenever an error occurs in event or method procedures of the class.

    All Visual FoxPro base classes have the following minimum set of properties:

    PropertyDescription
    ClassWhat type of class it is.
    BaseClassThe base class it was derived from, such as Form, Commandbutton, Custom, and so on.
    ClassLibraryThe class library the class is stored in.
    ParentClassThe class that the current class was derived from. If the class was derived directly from a Visual FoxPro base class, the ParentClass property is the same as the BaseClass property.

    Extending the Visual FoxPro Base Classes

    You can subclass these classes to set your own default control properties. For example, if you want the default names of controls you add to forms in your applications to automatically reflect your naming conventions, you can create classes based on the Visual FoxPro base classes to do this. You can create form classes with a customized look or behavior to serve as templates for all the forms you create.

    You could also subclass the Visual FoxPro base classes to create controls with encapsulated functionality. If you want a button to release forms when the button is clicked, you can create a class based on the Visual FoxPro command button class, set the caption property to “Quit” and include the following command in the Click event:

    THISFORM.Release

    You can add this new button to any form in your application.

    Creating Controls with Multiple Components

    Your subclasses aren’t limited to single base classes. You can add multiple controls into a single container class definition. Many of the classes in the Visual FoxPro sample class library fall into this category.

    Creating Non-Visual Classes

    A class based on the Visual FoxPro custom class doesn’t have a run-time visual element. You can create methods and properties for your custom class using the Class Designer environment. For example, you could create a custom class named StrMethods and include a number of methods to manipulate character strings. You could add this class to a form with an edit box and call the methods as needed. If you had a method called WordCount, you could call it when needed:

    THISFORM.txtCount.Value = ;
      THISFORM.StrMethods.WordCount(THISFORM.edtText.Value)

    Non-visual classes (like the custom control and the timer control) have a visual representation only at design time in the Form Designer. Set the picture property of the custom class to the .bmp file you want displayed in the Form Designer when the custom class is added to a form.

    Operating the Class Designer

    When you specify what class your new class is based on and the library to store the class in, the Class Designer opens.

    The Class Designer provides the same interface that the Form Designer does, allowing you to see and edit the properties of your class in the Properties window. Code editing windows allow you to write code to be executed when events occur or methods are called.

    Adding Objects to a Control or Container Class

    If you base the new class on the control or container class, you can add controls to it the same way you add controls in the Form Designer: choose the control button on the Form Controls toolbar and drag to size in the Class Designer.

    No matter what type of class you base the new class on, you can set properties and write method code. You can also create new properties and methods for the class.

    IF you press and hold the CTRL key while dragging objects from the toolbar to a form class, you disengage the default snap-to-grid behavior.

    Adding Properties and Methods to a Class

    You can add as many new properties and methods to the new class as you want. Properties hold values; methods hold procedural code to be run when you call the method.

    Creating New Properties and Methods

    When you create new properties and methods for classes, the properties and methods are scoped to the class, not to individual components in the class.

    To add a new property to a class

    1. From the Class menu, choose New Property.
    2. In the New Property dialog box, type the name of the property.
    3. Specify the visibility: Public, Protected, or Hidden. A Public property can be accessed anywhere in your application. Protected and Hidden properties and methods are discussed in Protecting and Hiding Class Members.
    4. Choose Add. You can also include a description of the property that will be displayed at the bottom of the Properties window in the Class Designer and in the Form Designer when the control is added to a form. Caution   When you add a property to a class that can be set by a user of the class, the user could enter an invalid setting for your property that could cause run-time errors. You need to explicitly document the valid settings for the property. If your property can be set to 0, 1, or 2, for example, say so in the Description box of the New Property dialog box. You might also want to verify the value of the property in code that references it.

    To create an array property

    • In the Name box of the New Property dialog box, specify the name, size, and dimensions of the array. For example, to create an array property named myarray with ten rows and two columns, type the following in the Name box: myarray[10,2]

    The array property is read-only at design time and is displayed in the Properties window in italics. The array property can be managed and redimensioned at run time. For an example of using an array property, see Managing Multiple Instances of a Form, Creating Forms.

    To add a new method to a class

    1. From the Class menu, choose New Method.
    2. In the New Method dialog box, type the name of the method.
    3. Specify the visibility: Public, Protected, or Hidden.
    4. Select the Access check box to create an Access method, select the Assign check box to create an Assign method, or select both check boxes to create Access and Assign methods.

    Access and Assign methods let you execute code when the value of a property is queried or when you attempt to change the property’s value.

    The code in an Access method is executed when the value of a property is queried, typically by using the property in an object reference, storing the value of the property to a variable, or displaying the value of property with a question mark (?).

    The code in an Assign method is executed when you attempt to change the value of a property, typically by using the STORE or = command to assign a new value to the property.

    You can also include a description of the method.

    Creating Classes

    You can create new classes in the Class Designer and you can see how each object will appear to the user as you design it.

    To create a new class

    • In the Project Manager, select the Classes tab and choose New. -or-
    • From the File menu, choose New, select Class, and choose New File. -or-
    • Use the CREATE CLASS command.

    The New Class dialog box lets you specify what to call the new class, the class to base the new class on, and the library to store it in.

    Modifying a Class Definition

    Once you have created a class, you can modify it. Changes made to a class affect all the subclasses and all the objects based on this class. You can add an enhancement to a class or fix a bug in the class, and all the subclasses and objects based on the class will inherit the change.

    To modify a class in the Project Manager

    1. Select the class you want to modify.
    2. Choose Modify. The Class Designer opens.

    You can also modify a visual class definition with the MODIFY CLASS command.

    Caution   Don’t change the Name property of a class if the class is already being used in any other application components. Otherwise, Visual FoxPro will not be able to locate the class when needed.

    Subclassing a Class Definition

    You can create a subclass of a user-defined class in one of two ways.

    To create a subclass of a user-defined class

    1. In the Project Manager New Class dialog box, click the dialog button to the right of the Based On box.
    2. In the Open dialog box, choose the class you want to base the new class on. -or-
    • Use the CREATE CLASS command. For example, to base a new class, x, on parentclass in Mylibrary.vcx, use this code: CREATE CLASS x OF y AS parentclass ; FROM mylibrary

    Class Member Protection and Hiding

    Properties and methods in a class definition are Public by default: code in other classes or procedures can set the properties or call the methods. Properties and methods that you designate as Protected can be accessed only by other methods in the class definition or in subclasses of the class. Properties and methods designated as Hidden can be accessed only by other members in the class definition. Subclasses of the class cannot “see” or reference hidden members.

    To ensure correct functioning in some classes, you need to prevent users from programmatically changing the properties or calling the method from outside the class.

    The following example illustrates using protected properties and methods in a class.

    The stopwatch class included in Samples.vcx, in the Visual FoxPro …\Samples\Classes directory, includes a timer and five labels to display the elapsed time:

    The Stopwatch class contains labels and a timer.

    Property Settings for the Stopwatch Class

    ControlPropertySetting
    lblSecondsCaption00
    lblColon1Caption:
    lblMinutesCaption00
    lblColon2Caption:
    lblHoursCaption00
    tmrSWatchInterval1000

    This class also has three protected properties, nSec, nMin, and nHour, and one protected method, UpdateDisplay. The other three custom methods in the class, Start, Stop, and Reset, are not protected.

    Tip   Choose Class Info on the Class menu to see the visibility of all properties and methods of a class.

    The protected properties are used in internal calculations in the UpdateDisplay method and the Timer event. The UpdateDisplay method sets the captions of the labels to reflect the elapsed time.

    The UpdateDisplay Method

    CodeComments
    cSecDisplay = ALLTRIM(STR(THIS.nSec)) cMinDisplay = ALLTRIM(STR(THIS.nMin)) cHourDisplay = ALLTRIM(STR(THIS.nHour))Convert the numeric properties to Character type for display in the label captions.
    THIS.lblSeconds.Caption = ; IIF(THIS.nSec < 10, ; “0” ,””) + cSecDisplay THIS.lblMinutes.Caption = ; IIF(THIS.nMin < 10, ; “0”, “”) + cMinDisplay THIS.lblHours.Caption = ; IIF(THIS.nHour < 10, ; “0”, “”) + cHourDisplaySet the label captions, retaining the leading 0 if the value of the numeric property is less than 10.

    The following table lists the code in the tmrSWatch.Timer event:

    The Timer Event

    CodeComments
    THIS.Parent.nSec = THIS.Parent.nSec + 1 IF THIS.Parent.nSec = 60 THIS.Parent.nSec = 0 THIS.Parent.nMin = ; THIS.Parent.nMin + 1 ENDIFIncrement the nSec property every time the timer event fires: every second.
    If nSec has reached 60, reset it to 0 and increment the nMin property.
    IF THIS.Parent.nMin = 60 THIS.Parent.nMin = 0 THIS.Parent.nHour = ; THIS.Parent.nHour + 1 ENDIF THIS.Parent.UpdateDisplayIf nMin has reached 60, reset it to 0 and increment the nHour property. Call the UpdateDisplay method when the new property values are set.

    The stopwatch class has three methods that are not protected: Start, Stop, and Reset. A user can call these methods directly to control the stopwatch.

    The Start method contains the following line of code:

    THIS.tmrSWatch.Enabled = .T.

    The Stop method contains the following line of code:

    THIS.tmrSWatch.Enabled = .F.

    The Reset method sets the protected properties to zero and calls the protected method:

    THIS.nSec = 0
    THIS.nMin = 0
    THIS.nHour = 0
    THIS.UpdateDisplay

    The user cannot directly set these properties or call this method, but code in the Reset method can.

    Specifying the Default Value for a Property

    When you create a new property, the default setting is false (.F.). To specify a different default setting for a property, use the Properties window. In the Other tab, click on your property and set it to the desired value. This will be the initial property setting when the class is added to a form or form set.

    You can also set any of the base class properties in the Class Designer. When an object based on the class is added to the form, the object reflects your property settings rather than the Visual FoxPro base class property settings.

    Tip   If you want to make the default setting of a property an empty string, select the setting in the Property Editing box and press the BACKSPACE key.

    Specifying Design-Time Appearance for a Class

    You can specify the toolbar icon and the container icon for your class in the Class Info dialog box.

    To set a toolbar icon for a class

    1. In the Class Designer, choose Class Info from the Class menu.
    2. In the Class Info dialog box, type the name and path of the .bmp file in the Toolbar icon box. Tip   The bitmap (.bmp file) for the toolbar icon is 15 by 16 pixels. If the picture is larger or smaller, it is sized to 15 by 16 pixels and might not look the way you want it to.

    The toolbar icon you specify is displayed in the Form Controls toolbar when you populate the toolbar with the classes in your class library.

    You can also specify the icon to be displayed for the class in the Project Manager and Class Browser by setting the container icon.

    To set a container icon for a class

    1. In the Class Designer, choose Class Info from the Class menu.
    2. In the Container icon box, type the name and path of the .bmp file to be displayed on the button in the Form Controls toolbar.

    Creating, Copying, and Removing Class Library Files

    Every visually designed class is stored in a class library with a .vcx file extension.

    Creating a Class Library

    You can create a class library in one of three ways.

    To create a class library

    • When you create a class, specify a new class library file in the Store In box of the New Class dialog box. -or-
    • Use the CREATE CLASS command, specifying the name of the new class library. For example, the following statement creates a new class named myclass and a new class library named new_lib: CREATE CLASS myclass OF new_lib AS CUSTOM -or-
    • Use the CREATE CLASSLIB command. For example, type the following command in the Command window to create a class library named new_lib: CREATE CLASSLIB new_lib

    Copying and Removing Class Library Classes

    Once you add a class library to a project, you can easily copy classes from one library to another or simply remove classes from libraries.

    To copy a class from one library to another

    1. Make sure both libraries are in a project (not necessarily the same project).
    2. In the Project Manager, select the Classes tab.
    3. Click the plus sign (+) to the left of the class library that the class is now in.
    4. Drag the class from the original library and drop it in the new library. Tip   For convenience and speed, you might want to keep a class and all the subclasses based on it in one class library. If you have a class that contains elements from many different class libraries, these libraries must all be open, so it will take a little longer to initially load your class at run time and at design time.

    To remove a class from a library

    To change the name of a class in a class library, use the RENAME CLASS command. Remember, however, that when you change the name of a class, forms that contain the class and subclasses in other .vcx files continue to reference the old name and will no longer function correctly.

    Visual FoxPro includes a Class Browser to facilitate using and managing classes and class libraries. For more information, see Class Browser.

    Adding Classes to Forms

    You can drag a class from the Project Manager to the Form Designer or to the Class Designer. You can also register your classes so that they can be displayed directly on the Form Controls toolbar in the Class Designer or Form Designer and added to containers the same way the standard controls are added.

    To register a class library

    1. From the Tools menu, choose Options.
    2. In the Options dialog box choose the Controls tab.
    3. Select Visual Class Libraries and choose Add.
    4. In the Open dialog box, choose a class library to add to the registry and choose Open.
    5. Choose Set as Default if you want the class library to be available in the Form Controls toolbar in future sessions of Visual FoxPro.

    You can also add your class library to the Form Controls toolbar by choosing Add in the submenu of the View Classes button. To make these classes available in the Form Controls toolbar in future sessions of Visual FoxPro, you still need to set the default in the Options dialog box.

    Default Property Setting Override

    When you add objects based on a user-defined class to a form, you can change the settings of all the properties of the class that are not protected, overriding the default settings. If you change the class properties in the Class Designer later, the settings in the object on the form are not affected. If you have not changed a property setting in the form and you change the property setting in the class, the change will take effect in the object as well.

    For example, a user could add an object based on your class to a form and change the BackColor property from white to red. If you change the BackColor property of the class to green, the object on the user’s form will still have a background color of red. If, on the other hand, the user did not change the BackColor property of the object and you changed the background color of the class to green, the BackColor property of the object on the form would inherit the change and also be green.

    Calling Parent Class Method Code

    An object or class based on another class automatically inherits the functionality of the original. However, you can easily override the inherited method code. For example, you can write new code for the Click event of a class after you subclass it or after you add an object based on the class to a container. In both cases, the new code is executed at run time; the original code is not executed.

    More frequently, however, you want to add functionality to the new class or object while keeping the original functionality. In fact, one of the key decisions you have to make in object-oriented programming is what functionality to include at the class level, at the subclass level, and at the object level. You can optimize your class design by using the DODEFAULT( ) function or scope resolution operator (::) to add code at different levels in the class or container hierarchy.

    Adding Functionality to Subclasses

    You can call the parent class code from a subclass by using the DODEFAULT( ) function.

    For example, cmdOK is a command button class stored in Buttons.vcx, located in the Visual FoxPro …\Samples\Classes directory. The code associated with the Click event of cmdOk releases the form the button is on. CmdCancel is a subclass of cmdOk in the same class library. To add functionality to cmdCancel to discard changes, for example, you could add the following code to the Click event:

    IF USED( ) AND CURSORGETPROP("Buffering") != 1
       TABLEREVERT(.T.)
    ENDIF
    DODEFAULT( )

    Because changes are written to a buffered table by default when the table is closed, you don’t need to add TABLEUPDATE( ) code to cmdOk. The additional code in cmdCancel reverts changes to the table before calling the code in cmdOk, the ParentClass, to release the form.

    Writing Class Definitions Programmatically

    You can define classes visually in the Class Designer and the Form Designer or programmatically in .PRG files. This section describes how to write class definitions. For more information about forms, see Creating Forms.

    In This Section

    Programmatic Class Definitions Displays the syntax of the basic shell for class creation. Protection and Hiding of Class Members Through example, illustrates how to protect or hide properties and methods in a class definition with keywords of the DEFINE CLASS command. Creation of Objects from Classes Describes how to create an object based on a saved visual class. Addition of Objects to a Container Class Details how to add objects to a container through use of the ADD OBJECT clause in the DEFINE CLASS command or the AddObject method. Method and Event Code Guidelines Lays out the rules for authoring event code and methods. Creating a Set of Table Navigation Buttons Provides an explanation of how to design and build table navigation buttons through code. Definition of a Grid Control Outlines how to define grid control programmatically. Object Reference Creation Explains how to make a reference to an object, and describes the benefits of creating references over making copies of objects. Arrays of Members and Objects Tells how to define members of classes as arrays and how to build arrays of objects. Data Storage with Objects Describes when and how to store data in objects. Object and Data Integration Contains information on why integrating objects and data is important.

    Related Sections

    Programming in Visual FoxPro Microsoft® Visual FoxPro® is a powerful interactive data management tool, but you also can access the full power of Visual FoxPro by creating applications. Understanding object-oriented programming techniques and the event-driven model can maximize your programming productivity. Application Development As you develop an application, organize its pieces with the Project Manager, an integrated way to build and test your application as you go. Object-Oriented Programming With object-oriented programming, you can create self-contained application components that respond to user actions and to the system and which can be easily maintained and reused. Understanding the Event Model The event model defines when and how user and system interactions occur.

    Programmatic Class Definitions

    In a program file, you can have program code prior to the class definitions, but not after the class definitions, in the same way that program code cannot come after procedures in a program. The basic shell for class creation has this syntax:

    DEFINE CLASS ClassName1 AS ParentClass [OLEPUBLIC]
       [[PROTECTED | HIDDEN PropertyName1, PropertyName2 ...]
          [Object.]PropertyName = eExpression ...]
       [ADD OBJECT [PROTECTED] ObjectName AS ClassName2 [NOINIT]
          [WITH cPropertylist]]...
       [[PROTECTED | HIDDEN] FUNCTION | PROCEDURE Name[_ACCESS | _ASSIGN]
          [NODEFAULT]
          cStatements
       [ENDFUNC | ENDPROC]]...
    ENDDEFINE

    Protection and Hiding of Class Members

    You can protect or hide properties and methods in a class definition with the PROTECTED and HIDDEN keywords of the DEFINE CLASS command.

    For example, if you create a class to hold employee information, and you don’t want users to be able to change the hire date, you can protect the HireDate property. If users need to find out when an employee was hired, you can include a method to return the hire date.

    DEFINE CLASS employee AS CUSTOM
    PROTECTED HireDate
      First_Name = ""
      Last_Name = ""
      Address = ""
      HireDate = { - - }
    
    PROCEDURE GetHireDate
      RETURN This.HireDate
    ENDPROC
    ENDDEFINE

    Creation of Objects from Classes

    When you have saved a visual class, you can create an object based on it with the CREATEOBJECT( ) function. The following example demonstrates running a form saved as a class definition in the class library file Forms.vcx:

    Creating and Showing a Form Object Whose Class Was Designed in the Form Designer

    CodeComments
    SET CLASSLIB TO Forms ADDITIVESet the class library to the .vcx file that the form definition was saved in. The ADDITIVE keyword prevents this command from closing any other class libraries that happen to be open.
    frmTest = CREATEOBJECT(“TestForm”)This code assumes that the name of the form class saved in the class library is TestForm.
    frmTest.ShowDisplay the form.

    Addition of Objects to a Container Class

    You can use the ADD OBJECT clause in the DEFINE CLASS command or the AddObject method to add objects to a container.

    For example, the following class definition is based on a form. The ADD OBJECT command adds two command buttons to the form:

    DEFINE CLASS myform AS FORM
      ADD OBJECT cmdOK AS COMMANDBUTTON
      ADD OBJECT PROTECTED cmdCancel AS COMMANDBUTTON
    ENDDEFINE

    Use the AddObject method to add objects to a container after the container object has been created. For example, the following lines of code create a form object and add two command buttons to it:

    frmMessage = CREATEOBJECT("FORM")
    frmMessage.AddObject("txt1", "TEXTBOX")
    frmMessage.AddObject("txt2", "TEXTBOX")

    You can also use the AddObject method in the method code of a class. For example, the following class definition uses AddObject in the code associated with the Init event to add a control to a grid column.

    DEFINE CLASS mygrid AS GRID
    ColumnCount = 3
    PROCEDURE Init
      THIS.Column2.AddObject("cboClient", "COMBOBOX")
      THIS.Column2.CurrentControl = "cboClient"
    ENDPROC
    ENDDEFINE

    Adding and Creating Classes in Method Code

    You can programmatically add objects to a container with the AddObject method. You can also create objects with the CREATEOBJECT( ) function in the Load, Init, or any other method of the class.

    When you add an object with the AddObject method, the object becomes a member of the container. The Parent property of the added object refers to the container. When an object based on the container or control class is released from memory, the added object is also released.

    When you create an object with the CREATEOBJECT( ) function, the object is scoped to a property of the class or a variable in the method that calls this function. The parent property of the object is undefined.

    Method and Event Code Guidelines

    In addition to writing code for the methods and events of an object, you can extend the set of methods in subclasses of Visual FoxPro base classes. Here are the rules for writing event code and methods:

    • The event set for the Visual FoxPro base classes is fixed and cannot be extended.
    • Every class recognizes a set of fixed default events, the minimum set of which includes Init, Destroy, and Error events.
    • When you create a method in a class definition with the same name as an event that the class can recognize, the code in the method is executed when the event occurs.
    • You can add methods to your classes by creating a procedure or function within the class definition.
    • You can create Access and Assign Methods for your classes by creating a procedure or function with the same name as a class property and _ACCESS or _ASSIGN appended to the procedure or function name.

    Calling Event Code up the Class Hierarchy

    When you create a class, the class automatically inherits all the properties, methods, and events of the parent class. If code is written for an event in the parent class, that code is executed when the event occurs with respect to an object based on the subclass. You can, however, overwrite the parent class code by writing code for the event in the subclass.

    To explicitly call the event code in a parent class when the subclass has code written for the same event, use the DODEFAULT( ) function.

    For example, you could have a class named cmdBottom based on the command button base class that has the following code in the Click event:

    GO BOTTOM
    THISFORM.Refresh

    When you add an object based on this class to a form, named, for example, cmdBottom1, you might decide that you also want to display a message for the user so that he or she knows that the record pointer is at the bottom of the table. You could add the following code to the Click event of the object to display the message:

    WAIT WINDOW "At the Bottom of the Table" TIMEOUT 1

    When you run the form, however, the message is displayed, but the record pointer doesn’t move because the code in the Click event of the parent class is never executed. To make sure the code in the Click event of the parent class is also executed, include the following lines of code in the Click event procedure of the object:

    DODEFAULT( )
    WAIT WINDOW "At the Bottom of the Table" TIMEOUT 1

    Note   You can use the ACLASS( ) function to determine all the classes in an object’s class hierarchy.

    Preventing Base Class Code from Executing

    Sometimes you’ll want to prevent the base class default behavior from taking place in an event or method. You can do this by including the NODEFAULT keyword in the method code you write. For example, the following program uses the NODEFAULT keyword in the KeyPress event of a text box to prevent the typed characters from being displayed in the text box:

    frmKeyExample = CREATEOBJECT("test")
    frmKeyExample.Show
    READ EVENTS
    DEFINE CLASS test AS FORM
      ADD OBJECT text1 AS TEXTBOX
      PROCEDURE text1.KeyPress
       PARAMETERS nKeyCode, nShiftAltCtrl
       NODEFAULT
       IF BETWEEN(nKeyCode, 65, 122) && between 'A' and 'z'
        This.Value = ALLTRIM(This.Value) + "*"
        ACTIVATE SCREEN      && send output to main Visual FoxPro window
        ?? CHR(nKeyCode)
       ENDIF
      ENDPROC
      PROCEDURE Destroy
       CLEAR EVENTS
      ENDPROC
    ENDDEFINE

    Creating a Set of Table Navigation Buttons

    A common feature of many applications is a series of navigation buttons that allow users to move through a table. These typically include buttons to move the record pointer to the next or prior record in the table, as well as to the top or bottom record in the table.

    Designing the Navigation Buttons

    Each of the buttons will have some characteristics and functionality in common, so it is a good idea to create a navigation button class. Then the individual buttons can easily derive this common appearance and functionality. This parent class is the Navbutton class defined later in this section.

    Once the parent class is defined, the following subclasses define the functionality and appearance specific to each of the four navigation buttons: navTop, navPrior, navNext, navBottom.

    Finally, a container class, vcr, is created and each of the navigation buttons is added to the container class. The container can be added to a form or a toolbar to provide table navigation functionality.

    NAVBUTTON Class Definition

    To create Navbutton, save the following six class definitions (Navbutton, navTop, navBottom, navPrior, navNext, and vcr) to a program file such as Navclass.prg.

    Definition of the Generic Navigation Commandbutton Class

    CodeComments
    DEFINE CLASS Navbutton AS COMMANDBUTTON Height = 25 Width = 25 TableAlias = “”Define the parent class of the navigation buttons.

    Give the class some dimensions. Include a custom property, TableAlias, to hold the name of the alias to navigate through.
    PROCEDURE Click IF NOT EMPTY(This.TableAlias) SELECT (This.TableAlias) ENDIF ENDPROCIf TableAlias has been set, this parent class procedure selects the alias before the actual navigation code in the subclasses is executed. Otherwise, assume that the user wants to navigate through the table in the currently selected work area.
    PROCEDURE RefreshForm _SCREEN.ActiveForm.Refresh ENDPROCUsing _SCREEN.ActiveForm.Refresh instead of THISFORM.Refresh allows you to add the class to a form or a toolbar and have it function equally well.
    ENDDEFINEEnd the class definition.

    The specific navigation buttons are all based on the Navbutton class. The following code defines the Top button for the set of navigation buttons. The remaining three navigation buttons are defined in the following table. The four class definitions are similar, so only the first one has extensive comments.

    Definition of the Top Navigation Button Class

    CodeComments
    DEFINE CLASS navTop AS Navbutton Caption = “|<“Define the Top navigation button class and set the Caption property.
    PROCEDURE ClickCreate method code to be executed when the Click event for the control occurs.
    DODEFAULT( ) GO TOP THIS.RefreshFormCall the Click event code in the parent class, Navbutton, so that the appropriate alias can be selected if the TableAlias property has been set.

    Include the code to set the record pointer to the first record in the table: GO TOP.

    Call the RefreshForm method in the parent class. It is not necessary to use the scope resolution operator (::) in this case because there is no method in the subclass with the same name as the method in the parent class. On the other hand, both the parent and the subclass have method code for the Click event.
    ENDPROCEnd the Click procedure.
    ENDDEFINEEnd the class definition.

    The other navigation buttons have similar class definitions.

    Definition of the Other Navigation Button Classes

    CodeComments
    DEFINE CLASS navNext AS Navbutton Caption = “>”Define the Next navigation button class and set the Caption property.
    PROCEDURE Click DODEFAULT( ) SKIP 1 IF EOF( ) GO BOTTOM ENDIF THIS.RefreshForm ENDPROC ENDDEFINE


    Include the code to set the record pointer to the next record in the table.



    End the class definition.
    DEFINE CLASS navPrior AS Navbutton Caption = “<“Define the Prior navigation button class and set the Caption property.
    PROCEDURE Click DODEFAULT( ) SKIP –1 IF BOF( ) GO TOP ENDIF THIS.RefreshForm ENDPROC ENDDEFINE


    Include the code to set the record pointer to the previous record in the table.



    End the class definition.
    DEFINE CLASS navBottom AS Navbutton Caption = “>|”Define the Bottom navigation button class and set the Caption property.
    PROCEDURE Click DODEFAULT( ) GO BOTTOM THIS.RefreshForm ENDPROC ENDDEFINE

    Include the code to set the record pointer to the bottom record in the table.

    End the class definition.

    The following class definition contains all four navigation buttons so that they can be added as a unit to a form. The class also includes a method to set the TableAlias property of the buttons.

    Definition of a Table Navigation Control Class

    CodeComments
    DEFINE CLASS vcr AS CONTAINER Height = 25 Width = 100 Left = 3 Top = 3Begin the class definition. The Height property is set to the same height as the command buttons it will contain.
    ADD OBJECT cmdTop AS navTop ; WITH Left = 0 ADD OBJECT cmdPrior AS navPrior ; WITH Left = 25 ADD OBJECT cmdNext AS navNext ; WITH Left = 50 ADD OBJECT cmdBot AS navBottom ; WITH Left = 75Add the navigation buttons.
    PROCEDURE SetTable(cTableAlias) IF TYPE(“cTableAlias”) = ‘C’ THIS.cmdTop.TableAlias = ; cTableAlias THIS.cmdPrior.TableAlias = ; cTableAlias THIS.cmdNext.TableAlias = ; cTableAlias THIS.cmdBot.TableAlias = ; cTableAlias ENDIF ENDPROCThis method is used to set the TableAlias property of the buttons. TableAlias is defined in the parent class Navbutton.

    You could also use the SetAll method to set this property:
    IF TYPE ("cTableAlias") = 'C'
    This.SetAll("TableAlias", "cTableAlias")
    ENDIF
    However, this would cause an error if an object were ever added to the class that did not have a TableAlias property.
    ENDDEFINEEnd class definition.

    Once you have defined the class, you can subclass it or add it to a form.

    Creating a Subclass Based on the New Class

    You can also create subclasses based on vcr that have additional buttons such as Search, Edit, Save, and Quit. For example, vcr2 includes a Quit button:

    Definition of a Table Navigation Control Subclass

    CodeComments
    DEFINE CLASS vcr2 AS vcr ADD OBJECT cmdQuit AS COMMANDBUTTON WITH ; Caption = “Quit”,; Height = 25, ; Width = 50 Width = THIS.Width + THIS.cmdQuit.Width cmdQuit.Left = THIS.Width – ; THIS.cmdQuit.WidthDefine a class based on vcr and add a command button to it.
    PROCEDURE cmdQuit.CLICK RELEASE THISFORM ENDPROCWhen the user clicks cmdQuit, this code releases the form.
    ENDDEFINEEnd class definition.

    Vcr2 has everything that vcr does, plus the new command button, and you don’t have to rewrite any of the existing code.

    Changes to VCR Reflected in the Subclass

    Because of inheritance, changes to the parent class are reflected in all subclasses based on the parent. For example, you could let the user know that the bottom of the table has been reached by changing the IF EOF( ) statement in navNext.Click to the following:

    IF EOF( )
       GO BOTTOM
       SET MESSAGE TO "Bottom of the table"
    ELSE
       SET MESSAGE TO
    ENDIF

    You could let the user know that the top of the table has been reached by changing the IF BOF( ) statement in navPrior.Click to the following:

    IF BOF()
       GO TOP
       SET MESSAGE TO "Top of the table"
    ELSE
       SET MESSAGE TO
    ENDIF

    If these changes are made to the navNext and navPrior classes, they will also apply automatically to the appropriate buttons in vcr and vcr2.

    Adding VCR to a Form Class

    Once vcr is defined as a control, the control can be added in the definition of a container. For example, the following code added to Navclass.prg defines a form with added navigation buttons:

    DEFINE CLASS NavForm AS Form
       ADD OBJECT oVCR AS vcr
    ENDDEFINE

    Running the Form Containing VCR

    Once the form subclass is defined, you can display it easily with the appropriate commands.

    To display the form

    1. Load the class definition: SET PROCEDURE TO navclass ADDITIVE
    2. Create an object based on the navform class: frmTest = CREATEOBJECT(“navform”)
    3. Invoke the Show method of the form: frmTest.Show

    If you don’t call the SetTable method of oVCR (the VCR object in NavForm) when the user clicks the navigation buttons, the record pointer moves in the table in the currently selected work area. You can call the SetTable method to specify what table to move through.

    frmTest.oVCR.SetTable("customer")

    Note   When the user closes the form, frmTest is set to a null value (.NULL.). To release the object variable from memory, use the RELEASE command. Object variables created in program files are released from memory when the program is completed.

    Definition of a Grid Control

    A grid contains columns, which in turn can contain headers and any other control. The default control contained in a column is a text box, so that the default functionality of the grid approximates a Browse window. However, the underlying architecture of the grid opens it up to endless extensibility.

    The following example creates a form that contains a Grid object with two columns. The second column contains a check box to display the values in a logical field in a table.

    Grid control with a check box in one column

    Definition of a Grid Class with a Check Box in a Grid Column

    CodeComments
    DEFINE CLASS grdProducts AS Grid Left = 24 Top = 10 Width = 295 Height = 210 Visible = .T. RowHeight = 28 ColumnCount = 2Start the class definition and set properties that determine the grid appearance.

    When you set the ColumnCount property to 2, you add two columns to the grid. Each column contains a header with the name Header1. In addition, each column has an independent group of properties that determines its appearance and behavior.
    Column1.ControlSource =”prod_name” Column2.ControlSource =”discontinu”When you set the ControlSource of a column, the column displays that field’s values for all the records in the table.
    Discontinu is a logical field.
    Column2.Sparse = .F.Column2 will contain the check box. Set the column’s Sparse property to .F. so that the check box will be visible in all rows, not just in the selected cell.
    Procedure Init THIS.Column1.Width = 175 THIS.Column2.Width = 68 THIS.Column1.Header1.Caption = ; “Product Name” THIS.Column2.Header1.Caption = ; “Discontinued” THIS.Column2.AddObject(“chk1”, ; “checkbox”) THIS.Column2.CurrentControl = ; “chk1” THIS.Column2.chk1.Visible = .T. THIS.Column2.chk1.Caption = “” ENDPROCSet column widths and header captions.







    The AddObject method allows you to add an object to a container — in this case, a check box named chk1.
    Set the CurrentControl of the column to the check box so that the check box will be displayed.
    Make sure that the check box is visible.
    Set the caption to an empty string so that the default caption “chk1” won’t be displayed.
    ENDDEFINEEnd of the class definition.

    The following class definition is the form that contains the grid. Both class definitions can be included in the same program file.

    Definition of a Form Class that Contains the Grid Class

    CodeComments
    DEFINE CLASS GridForm AS FORM Width = 330 Height = 250 Caption = “Grid Example” ADD OBJECT grid1 AS grdProductsCreate a form class and add an object, based on the grid class, to it.
    PROCEDURE Destroy CLEAR EVENTS ENDPROC ENDDEFINEThe program that creates an object based on this class will use READ EVENTS. Including CLEAR EVENTS in the Destroy event of the form allows the program to finish running when the user closes the form.
    End of the class definition.

    The following program opens the table with the fields to be displayed in the grid columns, creates an object based on the GridForm class, and issues the READ EVENTS command:

    CLOSE DATABASE
    OPEN DATABASE (HOME(2) + "data\testdata.dbc")
    USE products
    frmTest= CREATEOBJECT("GridForm")
    frmTest.Show
    READ EVENTS

    This program can be included in the same file with the class definitions if it comes at the beginning of the file. You could also use the SET PROCEDURE TO command to specify the program with the class definitions and include this code in a separate program.

    Object Reference Creation

    Instead of making a copy of an object, you can create a reference to the object. A reference takes less memory than an additional object, can easily be passed between procedures, and can aid in writing generic code.

    Returning a Reference to an Object

    Sometimes, you might want to manipulate an object by means of one or more references to the object. For example, the following program defines a class, creates an object based on the class, and returns a reference to the object:

    *--NEWINV.PRG
    *--Returns a reference to a new invoice form.
    frmInv = CREATEOBJECT("InvoiceForm")
    RETURN frmInv
     
    DEFINE CLASS InvoiceForm AS FORM
       ADD OBJECT txtCompany AS TEXTBOX
       * code to set properties, add other objects, and so on
    ENDDEFINE

    The following program establishes a reference to the object created in Newinv.prg. The reference variable can be manipulated in exactly the same way as the object variable can:

    frmInvoice = NewInv() && store the object reference to a variable
    frmInvoice.SHOW

    You can also create a reference to an object on a form, as in the following example:

    txtCustName = frmInvoice.txtCompany
    txtCustName.Value = "Fox User"

    Tip   Once you’ve created an object, you can use the DISPLAY OBJECTS command to display the object’s class hierarchy, property settings, contained objects, and available methods and events. You can fill an array with the properties (not the property settings), events, methods, and contained objects of an object with the AMEMBERS( ) function.

    Releasing Objects and References from Memory

    If a reference to an object exists, releasing the object does not clear the object from memory. For example, the following command releases frmInvoice, the original object:

    RELEASE frmInvoice

    However, because a reference to an object belonging to frmInvoice still exists, the object is not released from memory until txtCustName is released with the following command:

    RELEASE txtCustName

    Checking to See if an Object Exists

    You can use the TYPE( ), ISNULL( ), and VARTYPE( ) functions to determine if an object exists. For example, the following lines of code check to see whether an object named oConnection exists:

    IF TYPE("oConnection") = "O" AND NOT ISNULL(oConnection)
       * Object exists
    ELSE
       * Object does not exist
    ENDIF

    NoteISNULL( ) is necessary because .NULL. is stored to the form object variable when a user closes a form, but the type of the variable remains “O”.

    Arrays of Members and Objects

    You can define members of classes as arrays. In the following example, choices is an array of controls:

    DEFINE CLASS MoverListBox AS CONTAINER
    DIMENSION choices[3]
    ADD OBJECT lstFromListBox AS LISTBOX
    ADD OBJECT lstToListBox AS LISTBOX
    ADD OBJECT choices[1] AS COMMANDBUTTON
    ADD OBJECT choices[2] AS COMMANDBUTTON
    ADD OBJECT choices[3] AS CHECKBOX
    PROCEDURE choices.CLICK
       PARAMETER nIndex
       DO CASE
          CASE nIndex = 1
             * code
          CASE nIndex = 2
             * code
          CASE nIndex = 3
             * code
       ENDCASE
    ENDPROC
    ENDDEFINE

    When the user clicks a control in an array of controls, Visual FoxPro passes the index number of the control to the Click event procedure. In this procedure, you can use a CASE statement to execute different code depending on which button was clicked.

    Creating Arrays of Objects

    You can also create arrays of objects. For example, MyArray holds five command buttons:

    DIMENSION MyArray[5]
    FOR x = 1 TO 5
       MyArray[x] = CREATEOBJECT("COMMANDBUTTON")
    ENDFOR

    There are some considerations to keep in mind with arrays of objects:

    • You can’t assign an object to an entire array with one command. You need to assign the object to each member of the array individually.
    • You can’t assign a value to a property of an entire array. The following command would result in an error: MyArray.Enabled = .F.
    • When you redimension an object array so that it is larger than the original array, the new elements are initialized to false (.F.), as is the case with all arrays in Visual FoxPro. When you redimension an object array so that it is smaller than the original array, the objects with a subscript greater than the largest new subscript are released.

    Data Storage with Objects

    In object-oriented languages, a class offers a useful and convenient vehicle for storing data and procedures related to an entity. For example, you could define a customer class to hold information about a customer as well as a method to calculate the customer’s age:

    DEFINE CLASS customer AS CUSTOM
       LastName = ""
       FirstName = ""
       Birthday = { - - }
       PROCEDURE Age
          IF !EMPTY(THIS.Birthday)
             RETURN YEAR(DATE()) - YEAR(THIS.Birthday)
          ELSE
             RETURN 0
          ENDIF
       ENDPROC
    ENDDEFINE

    However, data stored in objects based on the customer class are stored only in memory. If this data were in a table, the table would be stored on disk. If you had more than one customer to keep track of, the table would give you access to all of the Visual FoxPro database management commands and functions. As a result, you could quickly locate information, sort it, group it, perform calculations on it, create reports and queries based on it, and so on.

    Storing and manipulating data in databases and tables is what Visual FoxPro does best. There are times, however, when you’ll want to store data in objects. Usually, the data will be significant only while your application is running and it will pertain to a single entity.

    For example, in an application that includes a security system, you would typically have a table of users who have access to the application. The table would include user identification, password, and access level. Once a user has logged on, you won’t need all the information in the table. All you need is information about the current user, and this information can be easily stored and manipulated in an object. The following class definition, for example, initiates a logon when an object based on the class is created:

    DEFINE CLASS NewUser AS CUSTOM
       PROTECTED LogonTime, AccessLevel
       UserId = ""
       PassWord = ""
       LogonTime = { - - : : }
       AccessLevel = 0
       PROCEDURE Init
          DO FORM LOGON WITH ; && assuming you have created this form
             This.UserId, ;
             This.PassWord, ;
             This.AccessLevel
          This.LogonTime = DATETIME( )
       ENDPROC
    * Create methods to return protected property values.
       PROCEDURE GetLogonTime
          RETURN This.LogonTime
       ENDPROC
       PROCEDURE GetAccessLevel
          RETURN This.AccessLevel
       ENDPROC
     
    ENDDEFINE

    In the main program of your application, you could create an object based on the NewUserclass:

    oUser = CREATEOBJECT('NewUser')
    oUser.Logon

    Throughout your application, when you need information about the current user, you can get it from the oUser object. For example:

    IF oUser.GetAccessLevel( ) >= 4
       DO ADMIN.MPR
    ENDIF

    Object and Data Integration

    In most applications, you can best utilize the power of Visual FoxPro by integrating objects and data. Most Visual FoxPro classes have properties and methods that allow you to integrate the power of a relational database manager and a full object-oriented system.

    Properties for Integrating Visual FoxPro Classes and Database Data

    ClassData properties
    GridRecordSource, ChildOrder, LinkMaster
    All other controlsControlSource
    List box and combo boxControlSource, RowSource
    Form and form setDataSession

    Because these data properties can be changed at design or run time, you can create generic controls with encapsulated functionality that operate on diverse data.

    For more information about integrating data and objects, see Creating Forms, and Using Controls

    Setting Properties

    You can set the properties of an object at run time or design time.

    To set a property

    • Use this syntax: Container.Object.Property = Value For example, the following statements set various properties of a text box named txtDate on a form named frmPhoneLog: frmPhoneLog.txtDate.Value = DATE( ) && Display the current date frmPhoneLog.txtDate.Enabled = .T. && The control is enabled frmPhoneLog.txtDate.ForeColor = RGB(0,0,0) && black text frmPhoneLog.txtDate.BackColor = RGB(192,192,192) && gray background

    For the property settings in the preceding examples, frmPhoneLog is the highest-level container object. If frmPhoneLog were contained in a form set, you would also need to include the form set in the parent path:

    frsContacts.frmPhoneLog.txtDate.Value = DATE( )

    Setting Multiple Properties

    The WITH … ENDWITH structure simplifies setting multiple properties. For example, to set multiple properties of a column in a grid in a form in a form set, you could use the following syntax:

    WITH THISFORMSET.frmForm1.grdGrid1.grcColumn1
     .Width = 5
     .Resizable = .F.
     .ForeColor = RGB(0,0,0)
     .BackColor = RGB(255,255,255)
     .SelectOnEntry = .T.
    ENDWITH

    Calling Methods

    Once an object has been created, you can call the methods of that object from anywhere in your application.

    To call a method

    • Use this syntax: Parent.Object.Method

    The following statements call methods to display a form and set the focus to a text box:

    frsFormSet.frmForm1.Show
    frsFormSet.frmForm1.txtGetText1.SetFocus

    Methods that return values and are used in expressions must end in open and closed parentheses. For example, the following statement sets the caption of a form to the value returned from the user-defined method GetNewCaption:

    Form1.Caption = Form1.GetNewCaption( )

    Note   Parameters passed to methods must be included in parentheses after the method name; for example, Form1.Show(nStyle). passes nStyle to Form1’s Show method code.

  • Visual FoxPro FoxTalk 2001

    Visual FoxPro FoxTalk 2001

    Language Enhancements in VFP 7, Part 1 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Visual FoxPro 6 Language Reference Book on Archive.Org

    Language Enhancements in VFP 7, Part 1

    Doug Hennig

    Want an early peek at what’s new in VFP 7? This is the first article in a series where Doug Hennig discusses the language enhancements Microsoft is implementing and how to take advantage of some of them now.

    In my article in the December 2000 issue of FoxTalk (“Reusable Tools: Have Your Cake and Eat it, Too”), I discussed “version envy”: wanting to use features planned in an upcoming version of a product in the current version. In that article, I discussed several new functions in VFP 7 and showed PRGs that can provide their functionality in VFP 6. After reading this article, FoxTalk editor Whil Hentzen thought it would be a great idea to extend this to covering all of the new commands and functions in VFP 7, both to familiarize you with them and to enable you, where possible, to use them in VFP 6.

    So, we’ll start the new year off with this idea and spend several months looking at language enhancements in VFP 7. We won’t look at everything that’s new in VFP 7 (such as IntelliSense, editor improvements, DBC events, and so forth); it would take a whole book to cover such a broad topic. Instead, we’ll focus on functions and commands with three things in mind:

    • We’ll briefly consider the purpose of each command and function without getting too bogged down in details (for example, I won’t discuss parameters or return values in detail; you can get that from the VFP 7 Help).
    • We’ll look at practical uses for the commands and functions.
    • Where possible, we’ll explore ways to get the functionality now in VFP 6.

    Keep in mind that, as of this writing, VFP 7 isn’t even in beta yet (it’s considered a Tech Preview version), and these articles are based on the version distributed at DevCon in September 2000. So, commands and functions might be added, removed, or altered.

    Let’s start off with improvements to existing commands and functions, going through them in alphabetical order. Once we’re finished with these, we’ll move on to new commands and functions.

    ALINES()

    ALINES() has been a favorite function of mine since it was introduced. It parses a string (which may be in a variable or memo field) into lines terminated with a carriage return (CR), linefeed (LF), or carriage return-linefeed combination (CRLF) and puts each line into its own element in an array. ALINES() can now accept one or more parse characters, so the “lines” can be delimited with something other than CR and LF. For example, a comma-delimited field list can easily be converted to an array. Why would you want to do this? Because it’s easier to process an array than a comma-delimited string. Here’s a brute-force way of processing the items in such a string:

      
    

    This is ugly code to both read and write. Here’s a more elegant approach that converts commas to CR, then uses ALINES():

      
    

    In VFP 7, the first two lines in this code can be reduced to the following:

      
    

    AMEMBERS()

    This is one of those functions you likely don’t use very often, but it’s very useful at those times when you need it. AMEMBERS() fills an array with the properties, events, and methods (PEMs) of an object or class. There are two changes to AMEMBERS() in VFP 7: You can now get the PEMs of a COM object, and you can specify a filter for the PEMs.

    Passing 3 for the third parameter indicates that the second parameter is a COM object. Here’s an example that puts the PEMs for Excel into laPEMs:

      
    

    A new fourth parameter allows you to filter the list of PEMs so the array contains only a desired subset. For example, you might want only protected, hidden, or public PEMs, native or user-defined PEMs, changed PEMs, and so forth. This is handy, because prior to VFP 7, the only way to filter a list of PEMs was to use PEMSTATUS() on each one. For example, I use the following routine, CopyProperties, in VFP 6 to copy the properties of one object to another instance of the same class. Why would you want to do that? Imagine a form that you pass an object to and have the user modify the properties of the object in the form’s controls. What if the user wants to press a Cancel button to undo his or her changes? I decided to copy the object’s properties to another object of the same class, and then have the form work on the new object and, if the user chooses OK, copy the properties of the edited object to the original object. If the user chooses Cancel instead, the original object isn’t touched. So, the form creates another instance of the passed object’s class and then calls CopyProperties to copy the properties of the original object to the new instance. Here’s the code for CopyProperties (you’ll also find it in COPYPROPERTIES6.PRG in the accompanying Download file):

      
    

    The VFP 7 version is a bit simpler. First, it uses the new “G” flag so the array only contains the public properties of the source object—we don’t have to use PEMSTATUS() to later ignore protected or hidden properties. Next, although there’s currently a bug that prevents it from working with array properties, we’ll be able to use the “C” flag so the array only contains properties that have changed from their default values; when this bug is fixed (notice that I’m being optimistic and didn’t say “if” <g>), we’ll be able to remove the PEMSTATUS() check for changed properties. Finally, I’ve submitted an enhancement request (ER) to Microsoft to provide a flag for read-write properties. If this ER is implemented, we’ll be able to remove the PEMSTATUS() check for read-only properties. Thus, the VFP 7 version will be simpler and faster than its VFP 6 counterpart. Here’s the code for COPYPROPERTIES7.PRG (I removed a few comments that duplicate those in the VFP 6 version in order to conserve space):

      
    

    By the way, if you examine COPYPROPERTIES7.PRG, you’ll see that the header comments include my e-mail address and Web site, and that they appear blue and underlined, just like a hyperlink in your browser. Clicking on either of these gives the expected action (a Send Message dialog box with my e-mail address already filled in or my Web site in your browser). This editor enhancement makes it simple to direct other developers to your Web site for support, more information or documentation, updates, and so on.

    TESTCOPYPROPERTIES.PRG shows how CopyProperties can be used. Change the statement calling CopyProperties6 to CopyProperties7 to see how the VFP 7 version works.

    Here’s another use of AMEMBERS() that’s somewhat similar. PERSIST.PRG provides a way to persist the properties of an object so they can be restored at another time (for example, the next time the user runs the application). It creates a string that can be stored, for example, in a memo field in a table. This string contains code that can be used to restore the properties of an object. For example, the string might look like this:

      
    

    After retrieving this string from wherever it’s stored, you’d then do something like this to restore the saved properties (in this example, lcPersist contains the string):

      
    

    This example uses the new VFP 7 EXECSCRIPT() function, which I discussed last month.

    I won’t show the code for PERSIST.PRG here, both because of space limitations and because it’s quite similar to COPYPROPERTIES7.PRG. To see this routine in action, run TESTPERSIST.PRG.

    ASCAN()

    Two things I’ve wished for a long time that Microsoft would add to ASCAN() are the ability to specify which column to search in and to optionally return the row rather than the element (to avoid having to subsequently call ASUBSCRIPT() to get the row). My wish was granted in VFP 7, plus ASCAN() gains the ability to be exact or case-insensitive. The new fifth parameter specifies the column to search in, and the new sixth parameter is a “flags” setting that determines whether the return value is the element or the row and whether the search is exact or case-insensitive.

    Because I always want the row and never want the element number, normally want a case-insensitive but exact search, and have a difficult time remembering exactly which values to use for the flags (no wisecracks from younger readers <g>), I created ArrayScan, which accepts an array, the value to search for, the column number to search in (the default is column 1), and logical parameters to override the exact and case-insensitive settings. Here’s the code in ARRAYSCAN7.PRG (I omitted header comments and ASSERT statements for brevity):

      
    

    In VFP 6, we can do something similar, but since we don’t have the new capabilities of ASCAN(), we have to use a different approach: We’ll use ASCAN() to find the value anywhere in the array, then determine whether it’s in the correct column. If not, we’ll change the starting element number and try again. ARRAYSCAN6.PRG has almost the same functionality as ARRAYSCAN7.PRG (although it’s slower and has more complex code), except support for case-insensitivity—to implement that feature, you’d have to do it via the brute-force method of going through each row in the array and looking for a case-insensitive match in the desired column. Here’s the code for ARRAYSCAN6.PRG:

      
    

    TESTARRAYSCAN.PRG demonstrates how both ARRAYSCAN6.PRG and ARRAYSCAN7.PRG work.

    ASORT()

    VFP 7 adds one new feature to ASORT(): a case-insensitivity flag (in VFP 6, ASORT() is always case-sensitive).

    BITOR(), BITXOR(), and BITAND()

    These functions can now accept more than the two parameters they do in VFP 6; they’ll accept up to 26 parameters in VFP 7. This is useful in cases (such as some API functions and COM objects) where several flags have to be ORed together; in VFP 6, you have to use something like BITOR(BITOR(BITOR(expr1, expr2), expr3), expr4) to do this.

    BROWSE

    At long last, we get a NOCAPTION option for BROWSE that displays the actual field names rather than the captions defined for the fields in the database container. You can get this behavior in VFP 6 by running BROWSEIT.PRG instead of using BROWSE. This relies on the fact that a browse window is really a grid, so we can change the caption of each column to the actual field name. Here’s the code for BROWSEIT.PRG:

      
    

    COMPILE

    In VFP 7, the COMPILE commands (COMPILE, COMPILE CLASSLIB, COMPILE REPORT, and so forth) respect the setting of SET NOTIFY. With SET NOTIFY OFF, no “compiling” dialog box is displayed. This is important for two reasons: In-process COM servers

    can’t display any user interface, and you likely don’t want your users to see such a dialog box. In VFP 6, we can suppress the dialog box by using the Windows API LockWindowUpdate function, which prevents updates to a window similar to VFP’s LockScreen property (although this won’t help in-process COM servers, since the dialog is still being called). The Download file includes LOCKWINDOW.PRG, which accepts .T. to prevent window updates and .F. to restore window updates. Here’s the code for this PRG:

      
    

    To prevent the “compiling” dialog box from being displayed, use code similar to the following:

      
    

    Conclusion

    Next month, we’ll carry on examining improved commands and functions in VFP 7. When we’re finished with them, we’ll move on to new commands and functions.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the January 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Driving the Data Bus: Chatting on Company Time–Building a VFP Chat Module 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Driving the Data Bus: Chatting on Company Time—Building a VFP Chat Module

    Andrew Coates

    Passing data to and from applications over the network makes the synchronization of data simple. In this article, Andrew Coates uses a messaging hub server to develop a chat component that can carry more than just typed conversations between application users.

    The chat system allows users across the network to exchange data in real time. Most commonly, this data consists of typed conversations, but the power of the system lies in its ability to pass other data as well. This could be in the form of audio or video or, as will be presented here, other text data that will allow the two chatting parties to retrieve the same database record by sending a primary key or keys from one to the other.

    The chat system

    To allow chatting, each client registers itself with a chat server as it starts up. The chat server maintains a list of connected clients and can detect disconnections automatically. If a client goes offline either by choosing to disconnect or due to network or some other failure, the server removes that client from the list. A schematic representation of the setup is shown in Figure 1.

    Each client consists of a chat handler, which sends and receives the messages, and zero or more chat forms. A chat conversation is carried out between chat forms, which are created specifically for that conversation and destroyed at the conclusion of the conversation. A client may be involved in many chats simultaneously, so to ensure that messages are routed to the correct form, the chat handler employs a concept of “chat slots” or a collection of instances of the chat form so they can handle multiple chats simultaneously and not get crossed lines. A schematic of the clients, the chat handlers, and the chat forms is shown in Figure 2.

    In Figure 2, there are two conversations being conducted. Client 1 is talking to client 2 and client 3. Because client 3 addresses its messages to client 1, chat slot 2, they never get mixed up with messages from client 2, which are addressed to client 1, chat slot 1.

    Initiating a chat

    To establish a conversation between two clients, one of the clients needs to initiate the chat. The initiating client sends a request to the intended recipient, and the recipient either accepts or declines the invitation to chat.

    The process from the chat initiator’s point of view is shown in Figure 3.

    When a client decides to start a chat, it creates a chat form and assigns the form a slot number. Next, it sends a request to the recipient that includes the sender’s ID and slot index. It waits for a response from the recipient, and if it gets the response, it shows the form and the chat begins. If it doesn’t get a response, it destroys the form and tells the user that the request timed out.

    Responding to a request for a chat

    The process from the chat recipient’s point of view is shown in Figure 4.

    When the recipient receives a request to chat, it decides to either accept or decline the request. If it declines to chat, it simply sends a message back to the requester declining the invitation. If it decides to accept the invitation, it creates an instance of the chat form and assigns it to a chat slot of its own. As long as it creates the form successfully, it sends a message to the requester accepting the chat and informing it of the recipient’s chat slot index. It then displays the form, and the chatting begins.

    Chatting

    To chat, the two clients send messages to each other addressed using the client ID and the chat slot index. When a client receives a message, it passes it to the form in the applicable chat slot, and the form displays the message.

    Ending the chat

    When a chat form is closed, it removes itself from the chat slot collection and sends a message to the other client that it’s being closed. When a form receives a message that the other party has closed the form, it remains open so any further action can be taken by the client, such as logging the conversation to a table or other file, but it won’t allow the form to send any more messages.

    InterCom System

    The InterCom System server (available from www.cns.nl/) is a messaging hub—that is, an application that sits somewhere on the network where all users can access it and allows applications to connect to it, register interest in certain events (subscribe), trigger events for other users, send messages to specific users, and disconnect from the hub. It sits somewhere on an IP network (note that the system is restricted to running over an IP network), and any client on the network—which could include the entire Internet—can connect to the server. Any connected client can then send a message to any other connected client. Clients can also trigger an “event,” which is broadcast to all of the other connected clients that have subscribed to that event. The triggered event can have data associated with it. Therefore, clients can communicate:

    • with all other interested clients (by triggering an event to which other interested parties have subscribed);
    • with a specific client (by sending a message to a specific client ID); or
    • with the server to get information about the other clients connected to the system.

    The client is in the form of an ActiveX control and can therefore be placed on a VFP form and its methods, properties, and events accessed as for any other such control. The InterCom System provides a client with three types of functionality—Notification, Messaging, and Inventory. The InterCom System is discussed in more detail in my article “Pushing the Point” in the August 1999 issue of FoxTalk.

    At the time of this writing, the InterCom System costs $399 USD for a royalty-free, single-developer license. There’s also an evaluation version of the system available for free download. The evaluation system is limited to a maximum of three simultaneous connections, but it’s otherwise identical to the full version.

    Wrapping the client

    The InterCom client has one feature that makes it awkward to use in VFP. It employs arrays passed by reference to return lists of information. VFP doesn’t handle this data type well (the COMARRAY() function only appears to work with COM servers created via a CREATEOBJECT call, not with ActiveX controls). To overcome this limitation, a wrapper for the ActiveX control was developed in VB. The wrapper intercepts the method calls and translates the array into a delimited string, which VFP handles very well. The wrapper class is included in the accompanying Download file.

    The chat handler

    The core of the chat component on each client is the chat handler. This object is instantiated when the application starts up and provides the communications link to the InterCom server. It’s based on the Form base class, so it can provide a container for the ActiveX InterCom client control. The load method of the class simply checks that the wrapped InterCom client is installed on the system. The init method accepts a reference to the calling object (for callbacks) and the name or IP address of the InterCom server machine. It then attempts to connect to the server and returns a logical indicating success.

    To initiate a chat, the application calls the chat handler’s StartChat() method, passing the descriptor of the client with whom the chat is requested. The sequence of events shown in Figure 3 then begins. The code for the StartChat() method is shown in Listing 1.

    Listing 1. The chat handler’s StartChat() method.

      
    

    In the example shown here, there’s the facility to pass two additional pieces of information with the chat request—a Company ID and a Contact ID. In this application, these are primary keys to two main tables. Passing these keys to the other client allows that client to retrieve the data about which the initiator wishes to chat, perhaps even displaying information from the relevant rows in the table as part of the chat dialog box.

    After checking the validity of these key parameters, the method requests a list of clients matching the descriptor from the InterCom server. The request is rejected if no clients match. If there’s a matching client, the method obtains a chat slot and populates it with an instance of the chat form. The chat form sends a message to the remote client’s chat handler requesting the chat and sets a timer that will fire if the operation times out. It’s then up to the other client to respond within the timeout period.

    The chat handler form class contains only one (significant) control—the InterCom client control. That control has only one overridden event—the one that processes incoming messages, which is called, originally enough, the OnMessage() event. The event code simply directs the message to the appropriate chat handler method. The OnMessage() event code is shown in Listing 2.

    Listing 2. The chat handler’s InterCom client OnMessage() event code.

      
    

    The important point to note from the OnMessage() event code is that the type of message being sent is stored in the message subject. All the OnMessage() handler does is work out what kind of message is being sent by reading the subject and then route the message to the appropriate message-handling method of the chat handler object.

    The chat handler object has four main message-handling methods:

    • HandleRequest()—Handles an invitation to chat from a remote client.
    • HandleAccept()—Handles the acceptance of an invitation to chat.
    • HandleMessage()—Handles a standard message (usually a line of typed conversation).
    • HandleDisconnect()—Handles the message sent by the other party when they terminate the chat session.

    HandleRequest()

    The HandleRequest() method is fired on the remote client when an invitation to chat is received. The method initiates the sequence shown in Figure 4. The code for the HandleRequest() method is shown in Listing 3.

    Listing 3. The chat handler’s HandleRequest() method.

      
    

    The HandleRequest() method starts by reading the message and breaking it down into its component parts. Each part is sent in the message’s data property on a separate line. The handler prompts the user, inviting them to accept the chat, and if the user rejects the invitation, it simply sends a message back to the initiator rejecting the request. If the request is accepted, the chat handler finds the next available free chat slot (or creates a new slot if there isn’t one free already). It then populates that slot with a new instance of the chat form. If it’s instantiated successfully, the chat form handles the notification of the acceptance of the chat. If it’s not instantiated successfully, the chat handler sends a message notifying the initiator that the chat was accepted, but that technical difficulties prevented it from occurring.

    HandleAccept()

    The HandleAccept() method is fired on the initiating chat client when it receives acceptance of an invitation to chat from the remote client. The code for the HandleAccept() method is shown in Listing 4.

    Listing 4. The chat handler’s HandleAccept() method.

      
    

    The HandleAccept() method begins by reading the constituent parts of the message from the data parameter. It then checks to see whether the chat was accepted or rejected, either because the remote user declined or technical difficulties prevented the chat from occurring. If it was accepted, the remote chat slot is assigned to a property of the appropriate chat form, the timeout timer is disabled, and the chat form is displayed—everyone is ready to chat! If it’s rejected, a message is displayed to that effect, and the chat form is released and the chat slot cleared.

    HandleMessage()

    The HandleMessage() method is fired on receipt of a standard message—the type of message that’s passed back and forth between clients during the course of a chat. The code for the HandleMessage() method is shown in Listing 5.

    Listing 5. The chat handler’s HandleMessage() method.

      
    

    The HandleMessage() method simply breaks out the chat slot (so it knows where to send the message) and sends the text of the message to the appropriate chat form for handling.

    HandleDisconnect()

    The HandleDisconnect() method is fired when the chat handler receives notice that the remote client has disconnected from the chat. The code for the HandleDisconnect() method is shown in Listing 6.

    Listing 6. The chat handler’s HandleDisconnect() method.

      
    

    The HandleDisconnect() method simply breaks out the chat slot (so it knows where to send the message) and fires the HandleDisconnect() of the appropriate chat form.

    The chat form

    The other half of the chat client component is the chat form itself. This is the visible manifestation of the chat component where the user types messages and reads the messages typed by the other user. The chat form in our sample chat app is shown in Figure 5.

    The chat form handles much of the communication once the chat handler has established the conversation. To do this, it uses the following key methods:

    • Init()—Responsible for notifying the remote client of some pertinent details and for actually displaying the form.
    • HandleDisconnect()—Responsible for handling the notification that the remote client has ended the chat session.
    • ReceiveMessage()—Responsible for displaying the text of a message received from the remote client.
    • SendDisconnect()—Responsible for notifying the remote client that the local client is terminating the chat session.
    • SendMessage()—Responsible for sending a line of text to the remote client.

    Init()

    The Init() method has two different behaviors, depending on whether the chat form is being instantiated as a chat initiator or a chat receiver. In the end, the functionality of each type of chat form is identical, but the process of creating the form differs depending on its role. The code for the Init() method is shown in Listing 7.

    Listing 7. The chat form’s Init() method.

      
    

    The Init() method accepts quite a list of parameters. The first is the mode in which this form is being instantiated. The allowable values are CHAT_RECEIVER or CHAT_CALLER (defined in chat.h). This information is used to determine the behavior of the object later in the Init() process. The next parameter refers to the local chat handler’s chat slot to which this chat form has been assigned. Next, a reference to the chat handler object is passed so the chat form can access its properties and methods. The next two parameters are additional data used in this sample application to pass the primary keys of two sample tables.

    The keys can be used by the form to display the data applicable to the chat. The descriptor for the remote client is the next thing to be passed. This will be displayed in the form’s caption so the user can tell who this chat session is with. Finally, if the chat slot for this chat on the remote client is known, this is passed as the last parameter. If this is the chat receiver, the remote chat slot will be known, but if this is the chat initiator, the remote chat slot will be passed back as part of the chat acceptance message.

    The parameters are assigned to properties of the form for later use, and the caption is set. Next, an application-specific method, SetButtonState(), is called. In this case, this method is designed to allow the retrieval of the linked data if primary keys have been passed to the form.

    Now the code forks. If the form is a chat recipient, it sends a message back to the chat initiator, accepting the chat and telling the initiator the chat slot ID that’s been assigned for use on the chat receiver, and makes the form visible. If the form is a chat initiator, it sends a message to the chat receiver requesting the chat and sets a timer so the chat requester doesn’t wait forever for a response.

    HandleDisconnect()

    The HandleDisconnect() method informs the user that the remote user has disconnected and sets a local property of the chat form to indicate that the chat is no longer live. It doesn’t close the form, as the local user might wish to review the contents of the chat before closing the form. The code for the HandleDisconnect() method is shown in Listing 8.

    Listing 8. The chat form’s HandleDisconnect() method.

      
    

    ReceiveMessage()

    The ReceiveMessage() method adds a line of text to the list box chat log. The code for the ReceiveMessage() method is shown in Listing 9.

    Listing 9. The chat form’s ReceiveMessage() method.

      
    

    SendDisconnect()

    The SendDisconnect() method sends a line of text from the local client to the remote client. It also displays the line in the list box chat log for later reference. The code for the SendDisconnect() method is shown in Listing 10.

    Listing 10. The chat form’s SendDisconnect() method.

      
    

    The SendDisconnect() method builds a message string that simply consists of the chat slot ID on the remote client. It then calls the SendMessage() method of the chat handler’s InterCom client control. The message is addressed to the remote client’s client ID; it has a subject of CHAT_DISCONNECT (defined in chat.h), and the text of the message consists of the remote chat slot ID.

    SendMessage()

    The SendMessage() method sends a line of text from the local client to the remote client. It also displays the line in the list box chat log for later reference. The code for the SendMessage() method is shown in Listing 11.

    Listing 11. The chat form’s SendMessage() method.

      
    

    The SendMessage() method builds a message string that consists of the chat slot ID on the remote client and the text of the message. It then calls the SendMessage() method of the chat handler’s InterCom client control. The message is addressed to the remote client’s client ID; it has a subject of CHAT_MESSAGE (defined in chat.h), and the text of the message consists of the remote chat slot ID and the line of text to be displayed. Finally, the line of text is added to the chat log list box on the local chat form.

    Sample code

    To demonstrate the use of the chat component, you’ll need the following:

    • The InterCom System server installed somewhere on a TCP/IP network. For the purposes of this exercise, it’s assumed that the server is installed at IP address 192.168.0.1.
    • The InterCom System client installed on all machines that are going to act as chat clients.
    • The InterCom client wrapper (available in the Download file) installed and registered on all machines that are going to act as clients.
    • An instance of VFP for each chat client (either running on the same machine or on separate machines). Note that the evaluation version of the InterCom server only allows three concurrent connections. The full version has no such limitations.
    • The class library CHAT.VCX (available in the Download file) extracted to a commonly accessible location. For the purposes of this exercise, it’s assumed that the path to the class library is \\SERVER\UTILS\VFPCHAT.

    Once these steps are complete, issue the following commands from the VFP command window for each instance of VFP. Substitute a unique number for n.

      
    

    Next, on one of the clients, enter the following command, where n is the number of another client:

      
    

    The second client should pop up a message box asking whether to accept the chat, and, if the chat is accepted, a chat form should be displayed on both the caller and receiver. Messages typed on one client should appear on the other (after the Enter key is pressed).

    Conclusion

    Being able to communicate with other users of an application in real time and with the facility to link the conversation with data from the application adds another powerful resource to the programmer’s toolbox. The techniques presented in this article combine a commercially available solution to inter-application communication with VFP’s data-handling and UI.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the February 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    ClassNavigator 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    ClassNavigator

    Jim Booth

    Multi-tier system designs often require that we use base classes that can’t be defined visually. This means using PRGs to define our classes. The Class Browser built into VFP is a wonderful tool for navigating the hierarchy of class definitions. However, when we use programmatically defined classes, the value of the Class Browser is lost. This month, Jim Booth introduces Michael G. Emmons of Flash Creative Management (now GoAmerica), who has a solution for us.

    Most of the classes we create in Visual FoxPro can be built using the visual class designer. For these visual classes, we have the Class Browser tool. However, there are a number of classes in Visual FoxPro that can’t be created in the visual designer. Classes like the column, page, session, and others must be created in program code using the DEFINE CLASS construct. For these classes the Class Browser fails, but the Class Navigator from Michael Emmons succeeds.

    Simple installation

    One thing that I dislike is a developer tool that takes a genius to install it and get it working. Michael has given us a utility that installs as simply as is possible. Just copy the APP file, ClassNavigator.app, to any directory on your machine, and it’s installed and ready to go.

    The test run

    To test this tool, I created two program files, as demonstrated in the following code:

      
    

    Notice that the class defined in TestClass2 is a subclass of the one defined in TestClass.

    Running ClassNavigator

    Next, I ran ClassNavigator.app and was greeted by the screen shown in Figure 1.

    There are four tabs—labeled Classes, Files, Options, and About—that are used in viewing the classes. The first tab we’ll visit is the Files tab, and we’ll select the Add button. In the file selection dialog box, I chose TestClass.prg; the resulting display is shown in Figure 2.

    Switching to the Classes tab and expanding the tree gives the display shown in Figure 3.

    The display has been expanded to show the existing details. You can see the filename and base class under Info, the Customer property under Properties, and the Custom method under Methods, just like the Class Browser would show us for a visual class definition.

    Double-clicking on the class name in this display will open the editor with the program loaded for editing.

    Hierarchies from multiple programs

    Now return to the Files tab and open the TestClass2.prg file. The new Classes display is shown in Figure 4.

    The Classes tab now shows us the hierarchy of these two classes, including the file information regarding each class.

    Where to get ClassNavigator

    This tool is included in the accompanying Download file. It’s also available at www.comcodebook.com, where future updates will be posted first (as well as the COM Codebook application framework). The source code for the Class Navigator is included in the download. The tool is freeware; you’re free to use or modify it to your desire, but you’re restricted from selling it to anyone else.

    Summary

    Michael G. Emmons has given us a utility that allows us to view classes that are defined in programs rather than visual class libraries. With his tool, we can see the inheritance hierarchy of these classes even when they cross multiple program files. A simple double-click opens any one of the classes for editing.

    Michael and Flash Creative Management (now GoAmerica) have graciously made this tool, as well as many other tools including a complete application framework, available to us all at no charge.

    The Component Gallery: VFP’s Best Kept Secret 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    The Component Gallery: VFP’s Best Kept Secret

    Markus Egger

    Visual FoxPro 6.0 shipped with a great new tool called the “Component Gallery.” This tool is mainly thought to be a productivity-enhancing tool that makes the use of components, classes, and other items found in Visual FoxPro much easier. Unfortunately, so far, very few third-party vendors and development teams have utilized the Component Gallery to the extent one would expect, making this great new addition to VFP one of VFP’s best kept secrets. In this article, Markus Egger not only demonstrates how the Component Gallery can be used, but also how you can create your own catalogs.

    When you start the Component Gallery from the Tools menu, it starts up with several default catalogs already loaded. Catalogs are simply collections of items stored in hierarchical fashion. You can think of each item stored in the Component Gallery as a shortcut to a Visual FoxPro item such as a class, a form, or a report. You might wonder what makes this tool valuable since we already have access to these items through the Class Browser as well as the Project Manager. That’s correct; however, there’s a significant difference: The Class Browser as well as the Project Manager are built as tools for the developer who needs access to every single item in the project. They show all classes, support classes, include files, bitmaps, and much more. The Component Gallery, on the other hand, simply provides access to the top-level items such as classes or components, but hides the complexity under the hood. You can compare this to a car mechanic who needs access to every part of the car, including the engine, transition, and car alarm, while the driver who simply uses the car to get to work every day doesn’t worry about all of those things.

    If you’re the “application mechanic,” you’ll be better off using the Class Browser or the Project Manager. It will provide access to every little bit of the application. This is especially true if you wrote the entire application by yourself. However, if you’re using third-party tools or even components written by your fellow co-workers, you might not need this level of granularity. In fact, it will make it much harder to use those components. Imagine that a team member provides you with a class library that you can utilize for a specific task. This library contains a number of classes, one of which is useful to you. All of the other classes are really just helper classes that the main class depends on, such as parent classes, members of the class, or other classes that are utilized by the main class to do its job. In addition, you get a number of external files such as include (.H) files, bitmaps, and XML definition files that are also required to make this component work.

    Do you really care about all of this information? Of course not! Or, well… you shouldn’t. Why would you want to become the mechanic of somebody else’s “car”? However, in reality, you’ll have to familiarize yourself with all of those components to make sure you add them all to your project and to pick the right class in the first place. Little do you know at this point that your co-worker tried to make this class as easy to use as possible and even provided a builder that can be used to configure this class.

    The Component Gallery will help you in this scenario. Your colleague can create a simple catalog that provides a link to the main class. When you drag and drop that link on your form, not only will the class get added, but, at the same time, all external references are taken care of, and immediately the provided builder starts to make it easy for you to configure the required properties rather than finding out manually which properties have to be set.

    You say this isn’t a problem for you, because you don’t work in a team, or perhaps the team is so small you can simply ask the co-worker? Well, what about third-party products? At EPS, we produce a product line called the FEC (Fox Extension Classes). This is simply a set of classes stored in class libraries, plus a few external dependencies, just as described in the preceding scenario. We gave a lot of thought to the product architecture, resulting in a very flexible environment that farms most of the functionality out to special behavior objects that can be configured and extended at will. The downside of this architecture is that 80 percent of the classes shipped in the libraries aren’t made to be used directly, but are designed for internal use only. How would you like to navigate all of the information through the Class Browser? So there!

    Another great advantage of the Component Gallery is its hierarchical representation of the information. It allows organizing items into folders. This is surely much easier to use than the inheritance-view provided by the Class Browser, or the flat-view provided by the Project Manager.

    So let’s have a look at the Gallery. When you start it the first time, it shows several default catalogs that provide access to the FFC (Fox Foundation Classes) as well as VFP’s samples (see Figure 1).

    Most of the items referenced in the default catalogs are FFC classes. Note that the Gallery can maintain a large number of different items, such as forms, reports, wizards, files, utilities, ActiveX controls, and much more. Basically, any Visual FoxPro as well as Windows item can be referenced through the Gallery. In addition to static links to items, the Gallery also supports a feature called “Dynamic Folder.” Figure 1 shows a reference to such a folder. It’s named “Installed Controls.” This folder automatically shows a list of all COM components and ActiveX controls installed on your system.

    To use a specific class such as the FFC VCR Buttons class, simply select the item and drag and drop it to your form or class that’s open in the designer (note that the Gallery can also be used for source code editing). It will automatically add an instance of the class to your form. You don’t have to worry about what library it’s stored in, nor are you bothered with all of the other classes stored in the same library. The Gallery abstracted all of that complexity away and provides simple, categorized access to this component. Note also that the Gallery displays a description of the component you selected.

    You want to know how much easier this is than using the Class Browser? Well, just right-click on the item and select “View in Browser.” This automatically opens the library in which the class is stored and switches the Component Gallery into Class Browser mode (the Gallery and the Browser internally are really the same application). The result is shown in Figure 2.

    As you can see, this view is much more cryptic. Not only do you see all kinds of classes you have no interest in (how about those custom classes—what’s your guess, can they be used by themselves or not?), but you also see class names that are much harder to understand, and the Class Browser doesn’t even provide a description.

    Another good example is the list of samples that ship with VFP. Perhaps you’re not aware of this, but the FoxPro team has produced a large number of examples that explain many aspects of the Visual FoxPro environment and language. In previous versions, this information was hard to find. Samples are scattered over all kinds of directories, and who wants to run all of them just to figure out what they’re doing? The Component Gallery ships with a special catalog that lists all of the included samples and provides access to them in ways that make sense for each individual example (see Figure 3).

    As I mentioned earlier, the Component Gallery can also be used to automatically trigger builders whenever a complex class is dropped in a designer. Try dropping the “Field Mover” class from the Data Navigation folder in the main Visual FoxPro catalog. Immediately, the builder shown in Figure 4 starts up and asks you to provide important property values. You can drop the same class from the Class Browser or the Form Controls toolbar, but then you’d have to go through an almost endless list of properties and try to figure out what they’re for, whether or not they’re important, and what the appropriate settings are.

    Creating your own catalogs

    So, by now are you convinced of the usefulness of this tool and eager to provide your own catalogs for your libraries, or perhaps even commercial third-party products? Well, you’ve come to the right place.

    Creating a new catalog is easy. Simply click the option button and select the Catalogs page in the options dialog box (see Figure 5). Click the New… button and specify the name of the catalog file (which is a regular DBF file). Initially, the new catalog is listed by its filename. We’ll define a friendlier name a little later. Click the OK button to close the dialog box and open the new catalog right away.

    To rename the catalog, right-click the item and choose Properties (the Rename feature doesn’t appear to work). This launches the dialog box shown in Figure 6. Note that you might not see all of the same options shown in Figure 6. If this is the case, open the Options dialog box and check the “Advanced Editing Enabled” feature on the first page. You can use the Properties dialog box not only to change the name, but also to set descriptions as well as icons that are to be displayed by the Gallery.

    To make the new catalog useful, you’ll have to add some items. First of all, add a new folder that provides links to the classes you’d like to keep track of. You can create new items, including folders, by right-clicking in the right-hand pane (see Figure 7). Again, you have to open the Properties dialog box to rename the folder and set other properties such as the icon.

    You can now proceed to add items to that new folder, in the same way you created the folder itself: Right-click in the right-hand pane and choose to add a new Class. The Gallery will present you with a GetClass() dialog box to select the class. The item added to the Gallery will automatically be assigned the name of the class, but again, you can change this to a friendlier name through the Properties dialog box.

    This is all you have to do to create a catalog for your classes. Note that the Gallery is smart enough to attach the proper behavior to your new class item. You can double-click on your class to edit it; you can drag and drop it to the form or class designer to create an instance; you can right-click on the item and open the class in the Class Browser; and much more. As you can see in Figure 7, the Gallery can handle a large number of different items and attaches the right behavior to them. This way, report items can be printed or previewed, video files can be played, tables can be opened, and so forth.

    But what if you’d like to add items that the Gallery isn’t aware of? A little while ago I wrote a public domain utility called GenRepoX (you can download it for free from www.eps-software.com). It extends the Visual FoxPro Report Writer, but it uses the internal report engine. Reports can be modified in the same way regular Visual FoxPro reports can be modified, but to execute the reports, a special function has to be called. Otherwise, the special features provided by GenRepoX will be lost. The Gallery is aware of reports and allows modifying, printing, and previewing them, but of course it isn’t aware of GenRepoX. Luckily, there are ways to teach the Gallery new behaviors.

    All Component Gallery behaviors are encapsulated in individual objects. The source code of those classes ships with Visual FoxPro in the _gallery.vcx and vfpglry.vcx class libraries. The behavior you need for GenRepoX is very similar to a regular report behavior, which is defined in the _reportitem class in the vfpglry.vcx library. To reuse what’s already there, you can simply subclass this behavior. I chose to call the subclass “GenRepoXItem.”

    There are two methods we’re interested in: Run() and SetMenu(). Those methods do what you think they would. Run() executes the item, and SetMenu() creates the right-click menu. Our major modification will be making the Run() method aware of GenRepoX, which can be done like so:

      
    

    I basically overwrite all of the default behavior and replace it with my own, which is rather simple in this case, since all I do is execute the report in preview mode. In the second line, I create a command that executes the report. Note that the cFileName property tells us what report filename the item is linked to (once it’s used in a catalog). In line 3, I execute the report by running it through the GenRepoX() method.

    This is enough to make the new item work. However, I’d also like to give the user a little visual clue that the item at hand isn’t a standard report, so I decided to modify the menu. The SetMenu() method is responsible for displaying the menu. Each behavior provided by the Gallery has default menu items. Some of those items are defined in each behavior, others (such as Cut, Copy, and Properties) are provided by the parent class used for all items, named “_item.” In our scenario, I’d like all of the default items, but I don’t want report-specific items, since I want to introduce my own. In do this in the following fashion:

      
    

    In line 3, I execute the item defined in the _item class. Note that I specifically name the class rather than issuing a DoDefault(), because I intend to skip the behavior defined in the direct parent class, which is the report item.

    In the next line, I add a new menu item using the AddMenuBar() method, which exists on every Gallery item. Parameter one specifies the caption, and parameter two specifies the method that’s to be executed when the item is selected. In this case, I simply execute the Modify() method, which I inherited from the standard report item. Note the special “oTHIS…” syntax. oTHIS is a special pointer that allows me to access the current object. The SetMenu() method is called by a Gallery internal mechanism, so by the time the menu is actually displayed, my object isn’t accessible through the THIS pointer anymore, which is the reason for this special naming convention used by the Gallery.

    The last parameter is interesting, too. From within an item, “THIS.oHost” always links you to an instance of the Gallery itself. The Gallery has a property named “lRunFileDefault” that tells you whether the Gallery is configured to run items whey they’re double-clicked (.T.) or modify them (.F.). In the menu, I’ll print the default item in bold as required by the Windows interface guidelines. I specify this using the last parameter. So if lRunDefaultFile is set to .F., I pass a .T. as the parameter, and vice versa.

    I’m sure you can now figure out the last line by yourself. It simply displays another menu item labeled “Preview GenRepoX,” and it executes the Run() method (the one I coded earlier) when selected.

    Now all that’s left to do is tell the Gallery about the new item type. I do that in the Properties dialog box of the main catalog item (see Figure 8).

    From now on, the new item shows up in the New Item menu (see Figure 9).

    Once you’ve added a GenRepoX report item, you can right-click it to see and use the behavior you added. Note that the item also exhibits default behavior that makes a lot of sense for our use. The icon defaults to a report, for instance (see Figure 10). In addition, the item uses the custom menu and behavior. You can use the right-click menu or double-click the item to execute the GenRepoX report.

    The options outlined in this article only represent a small example of what’s possible with custom items. The possibilities are truly endless. You can find more information about this subject on the Microsoft Visual FoxPro Web site (https://msdn.Microsoft.com/vfoxpro), as well as in the documentation and in several books (such as my own <s>).

    So far, so good. The new catalog is coming along nicely. But what if a user I’ve provided this catalog to has questions? Perhaps I should provide a link to my Web site. I can do this easily by adding a dynamic folder to my catalog. I add the folder just like any other folder, but this time, I set the folder’s “Dynamic folder” property (in the Node page of the folder’s Properties dialog box) to www.eps-software.com. That’s all there is to it!

    Conclusion

    The Component Gallery is a very powerful tool. Unfortunately, there have been very few products and projects that make use of it. Partly, this appears to be due to the poor documentation, but it’s also due to the not terribly obvious advantages the tool provides. However, once you’ve started using the Gallery, you’ll have a hard time living without it.

    If you have questions regarding the Gallery’s use, custom catalogs, and even AddIns (a possibility I couldn’t discuss in this article), feel free to e-mail me at Markus@eps-software.com.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the April 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Understanding COM+ with VFP, Part 1 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Understanding COM+ with VFP, Part 1

    Craig Berntson

    When COM+ debuted in Windows 2000, it introduced many new capabilities to enhance n-tier applications. Now with VFP 7, you can use these enhancements to make your applications more robust. In this first installment of a series, Craig Berntson reviews COM and MTS, and then introduces COM+.

    For many years, we’ve heard about the importance of breaking applications into multiple tiers or services. By splitting the user interface, business rules, and database access, we can easily modify or completely replace one service without affecting the others.

    Historically, Visual FoxPro applications have been single-tier solutions, even if the data resides on the server. This is because developers have mixed the user interface, business rules, and data access into one application or even the same form.

    With the use of SQL Server, we move to a two-tier scenario. The data is normally accessed via ODBC by the use of Remote Views or SQL pass through. Stored procedures are often called on the server, and the SQL SELECT statement is resolved before sending any data across the wire. It’s the splitting of the processing onto the server and the workstation that makes this design two-tier.

    In a three-tier solution, the user interface only displays data and accepts input from the user. There might be some minor data validation, such as ensuring that the required fields are populated or limiting the user’s selection via a list or combo box. However, all of the actual processing of the data takes place in a separate component that holds all of the business rules. Calculations of totals or taxes, validation of data, or the generating of report data are examples of things that occur in the middle-tier business logic. Finally, the data tier is responsible for the reading and writing of data into the data store. The user interface should never directly access the data services, but should go through the business services layer to get at the data.

    This separating of multiple tiers is what Microsoft calls the Distributed interNetworking Architecture, or DNA. The different components of each service can reside on the same computer, making a logical separation of each service—or, on multiple computers, providing a physical separation of the tiers. Typically, the user interface resides on the client computer, while the business and data components reside on an application server with the data store on a second server. When access is via a Web browser, an additional server for IIS is often added to the mix.

    COMmon knowledge

    The way to access these components is via the Component Object Model. COM is a specification that allows components written in different languages to interact with each other. Therefore, we can create a component in VFP that can be accessed from a VB or Delphi application, or even from Word or Excel. ActiveX controls are another example of COM objects. When you control Word or Excel from your VFP application, it’s done via COM.

    The first thing to consider when creating a COM component is how it will fit in with the other pieces of your application. In other words, you need to determine whether it should run in-process or out-of-process.

    An in-process component is compiled as a DLL and must be hosted by an executable program. It runs in its host’s memory space—hence the name in-process—which makes instantiating (running) the component fast. Data is marshaled (passed) across the COM boundary. Because the component runs in the same memory space as the application, if the component crashes, it most likely will cause the application to crash. One other thing to keep in mind: In-process servers written in VFP can’t have any user interface exposed.

    An out-of-process server is compiled as an EXE and runs in its own memory space. When it’s instantiated, there’s some overhead required such as allocation of memory, process id, and so on. This all takes time, which makes instantiating an out-of-process server slower than an in-process server. In addition, it takes longer to marshal data across the process boundaries from the application to the component, so it runs slower. However, because the COM server is running in a different memory space than the client application, if the component crashes, the application will quite possibly keep running.

    Creating a COM component in VFP is quite easy. The OLEPUBLIC keyword tells VFP to compile the code with the proper COM information needed for access from other applications:

      
    

    When you build the component (see Figure 1), you can choose “Win32 executable/COM server (exe)” to create an out-of-process server. To build an in-process server, select either “Single-threaded COM server (dll)” or “Multi-threaded COM server (dll).” I’ll talk more about the difference between the two types of DLLs later. Building the component will automatically register it on the development computer. You then instantiate it using the CreateObject() function:

      
    

    Many of the rules and intricacies of COM are automatically handled for us by VFP. However, we have to manually follow one rule. That rule states that we should never change the interface of a component. If we do, we need to create a new ID for the component. By interface, I don’t mean user interface, but the public methods and parameters of the component. Let’s look at the preceding example. If we add a third parameter to the Multiply method, we change the interface and need to create a new component ID. This is done on the Build dialog box. The last option on the dialog box is “Regenerate Component IDs.” Check the option to create a new GUID for the component.

    When you start deploying your COM components on remote servers, you’ll access them via Distributed COM (DCOM). Historically, under DCOM, you distribute an out-of-process server and set up the calling information on the client computer. Chapter 16 of the VFP Programmer’s Guide goes into detail about how to do this. When you install the component on a remote server, the code runs on the server, not on the client workstation. Don’t have any UI in your server because it will display on the server, not the client workstation.

    MTS to the rescue

    Microsoft saw the need for a better way for remote components to run, so they created Microsoft Transaction Server (MTS). Originally available for NT and Windows 9x through the NT 4.0 Option Pack, MTS solved a number of problems by providing a host for COM DLLs. It also provided a wizard that set up all of the DCOM calls on the client station for you. Some other features of MTS include:

    • Just-in-Time Activation: A component is kept on disk and then brought into memory (activated) only when needed.
    • Object Request Broker (ORB): MTS will handle multiple calls to the same component from multiple clients.
    • Transaction Services: Commits and aborts are handled by the Distributed Transaction Coordinator (DTC) instead of the application. This makes it possible to have a transaction that spans multiple databases.
    • Role-based Security: The security scheme allows you to determine who can access your components based on NT logins and groups. If a user doesn’t have authorization to access a component, an error message is returned to the client indicating that the component can’t be instantiated.
    • Connection Pooling: Typically, an application will make a connection to the data store, and then hold that connection during the life of the application. MTS allows multiple clients to use the same connection.

    Creating components for use under MTS requires that you think differently about your application design. First, your application should be stateless. This means that your client program should instantiate the component, make calls to a method, and then release the component. The connection to the component should be as short a time as possible. You should avoid setting properties and pass all of the needed information as parameters. Note that COM doesn’t allow parameters to be passed to the Init method.

    You also need to think about threading. We typically think of threading as single or multi-threading. VFP creates single-threaded applications. That is, it can only do one thing at a time. This is like going to the grocery store and only having one checkout stand open. All customers must go through the same line. Only one customer at a time can be helped. The others wait in the queue for their items to be processed.

    Multi-threading allows your application to split processing into different pieces, all running simultaneously. Using the grocery store example, you can unload parts of your shopping cart into different lines and have all of your groceries rung up at the same time.

    MTS uses a third type of threading, apartment model. Again using our grocery store example, customers may choose any open checkout stand, but once you’ve chosen one, you always have to use the same one. Luckily, MTS will open a new line for us when all are used.

    So, how do we make use of MTS in our VFP components? First, we have to add some code. Let’s modify our Multiply example to handle MTS.

      
    

    The Context object contains information about our particular instance of the COM component. Also, note the call to SetComplete(). This will commit any open transactions. If we need to abort the transaction, we would call SetAbort() instead.

    When we build our component, we can’t use “Win32 executable/COM server (exe).” MTS requires that components be DLLs. That leaves us with two choices: single or multi-threaded.

    The single-threaded DLL is a good choice when the method call will be very fast or there’s the possibility that only one user will hit it.

    The multi-threaded DLL isn’t truly multi-threaded. It’s apartment-model threaded. Make this choice when the method call is slow or many users will simultaneously call your component.

    Once you’ve built your component, you install it on the server with the appropriate VFP runtime libraries. Then, you create an MTS package and import your component using the MTS Explorer. Using MTS Explorer, you can set security and transactional support, and export a client setup program.

    You can get more information on MTS from Randy Brown’s article, “Microsoft Transaction Server for Visual FoxPro Developers,”.

    Windows 2000

    When Microsoft introduced Windows 2000, it came with several new services. One of those is COM+. Basically, COM+ is the marrying of COM and MTS, but new COM+ features were also introduced. Under Windows NT, MTS ran on top of the operating system. Under Windows 2000, it’s integrated into the OS. COM+ is only available in Windows 2000. However, Windows 95, 98, Me, and NT users can use COM+ components running on a Windows 2000 server. COM+ not only includes (and enhances) the features of MTS, but also introduces new services: Queued Components (QC), Loosely Coupled Events (LCE), Object Pooling, and Dynamic Load Balancing. In the next installment of this series, we’ll begin to delve into these services in detail.

    COM+ Applications are administered through the Component Services Manager (see Figure 2). You’ll find it in the Administrative Tools group in the Windows Control Panel. Let’s walk through registering the component that we saw earlier.

    1. Expand the tree under Component Services until COM+ Applications is available.
    2. Click on COM+ Applications to make it the currently selected node, and then right-click on COM+ Applications.
    3. From the context menu, select “New Application” to launch the COM Application Wizard. Then click Next.
    4. Click “Create an empty application” (see Figure 3).
    5. Enter the name for your application. In the example, I’ve called it “MyFirstCOMApp.” Then select the Activation Type. Normally, you’ll select Server application because your component will run on a server. If you install the component on a workstation and want it to run in your application’s memory space, then select Library application (see Figure 4). Click Next.
    6. Select the User ID that the component will run under. When installing on a server, it’s a good idea to set up a user specifically for your component. Be sure to assign the proper rights to the user so that the component will have access to all of the drives, directories, and resources that will be needed (see Figure 5). Click Next, then Finish.

    We now have the application set up, but it doesn’t contain any components. We have to add the component to the application:

    1. Click the plus sign (“+”) next to our new COM+ Application to expand the tree.
    2. Click on Components, and then right-click on Components. Select New Component from the context menu to launch the Component Install Wizard. Click Next.
    3. The wizard gives you three options: Install new component, Import component(s) that are already registered, or Install new event class(es). We’ll use the third option when I talk about Loosely Coupled Events. The second option, Import component(s) that are already registered, is used when you’ve previously installed the component on the computer. However, at the time this was written, there was a bug in Windows 2000 that caused this option to not work correctly. That leaves option 1. Click the button next to this option (see Figure 6).
    4. You’ll next be prompted to select the DLLs to install. If you don’t have the proper VFP runtime files installed, you won’t be able to select and install your component (see Figure 7). Once you’ve selected your components, click Next, then Finish.

    Now that your component is installed, how do you access it? The same way as before. Just use CREATEOBJECT() to instantiate the component and you’re ready to go.

    Summary

    We’ve covered quite a bit of ground in this article, but most of it should be review. You might be wondering whether all of this COM stuff is still useful in a .NET world. The answer is Yes! COM still exists in .NET. In fact, .NET was originally called COM+ 2.0. In upcoming articles in this series, I’ll discuss security, distribution, loosely coupled events, transactions, queued components, and other COM+ features.

    A GUID (pronounced GOO-id) is a Globally Unique Identifier. It’s a 128-bit Integer and looks something like {1EF10DF8-8BF9-4CD7-860A-8DCD84EA3197}. The GUID is generated using a combination of the current date and time, a counter, and the IEEE machine identifier from the network card. The chances of two GUIDs being the same are extremely remote.

    So how is this GUID used? When you build a component, three files are produced. The first is the DLL, and the second is a Type Library (TLB). The TLB is a binary file that lists all of the public classes, properties, methods, and events in your automation server. The third file is a Registry file (VBR). This lists the GUIDs for your server and is used to register your component.

    When you register the component, the VBR file is used to make Registry entries about your component. Things like the directory location of the DLL, its threading model, and its public interfaces are placed in the Registry. When you instantiate the component, the server name—for example, Excel.Application—is looked up in the Registry. The GUID will then be used to get additional information about the component, such as the directory location and public interfaces.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the May 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Understanding COM+ with VFP, Part 2 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Understanding COM+ with VFP, Part 2

    Craig Berntson

    In Part 2 of this series, Craig Berntson examines client distribution, security, and error handling.

    In Part 1 of this series (see the May 2001 issue of FoxTalk), I reviewed COM and MTS and introduced COM+ services under Windows 2000. I also showed you how to create a new COM+ application and install a component on the server. Now in Part 2, I’ll look at how to get the client to use the component on the server, security, and error handling.

    Is the client always right?

    Last month, I showed you how to install your application on the server. But that doesn’t do much good if you can’t get to the component from the client. The good news is, Windows 2000 makes it easy to set things up on the client. Let’s go back to the component we built and installed last month. As a quick review, here’s the component code:

      
    

    I call this the world’s dumbest COM component. All it does is multiply two numbers. However, keeping the sample code simple allows us to concentrate on the COM+ aspects of the example.

    Now we need to do an install on the client. Let’s go back to the Component Services Manager. Expand the tree under COM+ Applications and select MyFirstCOMApp. This is the COM+ Application that we built and installed last month. Now right-click on MyFirstCOMApp and select Export. The COM Application Export Wizard will appear (see Figure 1). You’re first prompted to enter the full path and filename for the application file. Enter C:\COMApps\MyFirstCOMAppProxy. Then make sure you’ve selected “Application Proxy—Install on other machines to enable access to this machine.” (The other option, “Server Application,” is used when you want to install the component on another server.) Then click Next and then Finish.

    The wizard has created two files—MyFirstCOMAppProxy.MSI.CAB and MyFirstCOMAppProxy.MSI. These files can be copied and installed on the client computer. These files don’t include the component. You don’t need it on the client. They contain information on the public methods and properties of the component and pointers to the server so that Windows can find the component and instantiate it.

    Again, you never install the component on the client. Instead, you install a proxy. Your application thinks the component is installed and running locally, but it isn’t. Make note of this. A component installed on an application server never runs on the client. For some reason, this is a difficult concept for some people to understand.

    You instantiate the server component exactly the same way you instantiate a local component. Try this in the Command Window:

      
    

    Why did this work? VFP makes a call to Windows, asking for the component to be instantiated. Windows looks up the component information in the Registry and finds that the component lives on an application server. Windows then instantiates a proxy to the component and makes a call to the server to instantiate the component. VFP doesn’t know it’s talking to the proxy; it thinks it’s talking directly to the component. When you call the Multiply method, the proxy sends the call to the server via DCOM and the result is passed back to the proxy, which passes it on to VFP.

    Are you feeling insecure?

    Now that the component is installed on the server and the client proxies are installed, let’s see how easily we can grant or prohibit access to the component. COM+ uses role-based security. A role is a type of user. For example, in a bank you might have tellers, managers, loan officers, customer service people, and so forth. Each of these people is a role. You want to prohibit tellers from making loans, so in COM+, you could set up security on a loan component to prohibit this. The nice thing is that this is a configuration option and easy to change using the Component Services Manager.

    Roles are based on Windows users and groups, so the first step in setting up the security scheme is to establish the Windows security groups. It can be confusing to understand where roles fit in the hierarchy of groups and users. The COM+ Help file states, “Roles are categories of users that have been defined for the application for the purpose of determining access permissions to the application’s resources. The developer assigns the roles (as symbolic user categories) to the application.” That sounds a lot like a Windows user group to me, so to keep it easy, think of a role as a user group that’s application-specific.

    Now, getting back to our bank example, we’d have four groups: tellers, managers, loan officers, and customer service. Go ahead and create them on the server using Windows User Manager in NT or the Computer Management Applet in Windows 2000. Enter the first user group, and then the other three.

    Once those groups are created, go back to MyFirstCOMApp in the Component Services Manager. Click on Roles, and then right-click and select New Role. Enter the first role, Teller, and click OK (see Figure 2). It’s not necessary to name the roles the same as the Windows user groups, but it makes management easier. Now create the three remaining roles of Manager, Loan Officer, and Customer Service.

    Next we need to identify the users in each role. Expand the tree under Teller. You’ll see a folder labeled “Users.” Click on the folder, and then right-click and select New User. Scroll down the list of Windows users and groups and select the Tellers user group, and then click Add. Do the same for Managers and Customer Service. Then click OK. You’ll see each of the Windows Security groups added to the Teller role in Component Services (see Figure 3).

    When new users are added to the system, they’re added to the proper Windows group, which in turn automatically puts them in the proper role. So far, we’ve identified the roles, but we haven’t told Component Services to use any security. Click on MyFirstCOMApp, right-click and select Properties, and then select the Security tab. Check “Enforce access checks for this application,” and then click OK (see Figure 4). Ignore the other options for now, I’ll discuss them shortly.

    Now right-click on MyComm.Math and select Properties, then the Security tab. You’ll see that “Enforce component level access checks” is selected. You’ll also see the security roles listed. Simply check the roles that are to have access to this component (see Figure 5).

    You can drill down and assign the security access to the interface or method level if you want. This enables you to have a single component with several interfaces, each having different security levels. If a user doesn’t have the proper access to use a component, an error message is returned stating that the component couldn’t be instantiated.

    Now, let’s go back to the Application Security dialog box (see Figure 4). There are some additional options that I need to discuss. The first is Security level. This controls when COM+ validates the user’s security. With the first option, “Perform access checks only at the process level,” role-checking won’t be done at the component, interface, or method levels. Under the second option, “Perform access checks at the process and component level,” the security role is checked when a call is made to the component. You’ll almost always use the second option. However, the first option is useful when you’ve already validated the user.

    The next setting is “Authentication level for calls.” There are six options, as described in Table 1. As you move through the list, each option gets progressively more secure. Note that the higher the level of security, the longer it takes to validate the user. The default level is Packet.

    Table 1. Authentication levels.Expand table

    LevelDescription
    NoneNo authentication.
    ConnectChecks security only when the client connects to the component.
    CallCheck security at the beginning of every call.
    PacketChecks security and validates that all data was received.
    Packet IntegrityChecks security and validates that none of the data was modified in transit.
    Packet PrivacyChecks security and encrypts the packet.

    Finally, we have “Impersonation level.” This sets the level of authority that the component gives to other processes. There are four different levels as described in Table 2. The default is Impersonate.

    Table 2. Impersonation levels.Expand table

    LevelDescription
    AnonymousThe second process knows nothing about the client.
    IdentifyThe second process can identify who the client is.
    ImpersonateThe second process can impersonate the client, but only for processes on the same server.
    DelegateThe second process can impersonate the client in all instances.

    Thus far, I’ve discussed declarative, role-based security, which is defined and managed at runtime. You can also use programmatic security. This allows you to branch your code based on the access level of the user. For example, a loan officer might only be able to authorize a loan up to $50,000. After that, it takes a manager’s approval to authorize the loan.

      
    

    By using both role-based and programmatic security, you can support just about any security scheme that you need.

    Error handling

    One of the questions I often see on online forums is, “How do I report an error back to the user?” If you think about this, the answer doesn’t seem easy. You can’t display any dialog boxes with the error from your component. It’s running on a different computer than the user interface. VFP has the COMRETURNERROR() function to send the error message back to the client. COMRETURNERROR() takes two parameters. The first is the name of the module where the error occurred. The second is the message to display to the user. Let’s look at the code.

      
    

    Now compile the code into a DLL and instantiate it.

      
    

    You might expect “This will never be displayed” to show on the VFP desktop. However, an error dialog box appears instead.

    You can return any error information you want in the message parameter. You also might want to enhance the error method by capturing additional information using AERROR() or writing information to an error log. There’s one caveat: The Error method won’t fire if an error occurs in the Init method.

    Conclusion

    That pretty much covers installation, security, and error handling. Next month, I’ll discuss transactions and see how COM+ and VFP 7 allow us to include VFP data in transactions, something that couldn’t be done with MTS and VFP 6.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the June 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Understanding COM+ with VFP, Part 3 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Understanding COM+ with VFP, Part 3

    Craig Berntson

    Transactions are an important part of any data update mechanism. Part 3 of this series by Craig Berntson introduces the Distributed Transaction Coordinator and explains how to use transactions under COM+.

    Transactions, transactions, transactions. Without them, we can’t be sure that data is getting written to all of the Tables involved in an update. Before digging into how transactions work in COM+, let’s do a quick review of transactions.

    A review of transactions

    Joe wants to transfer $100 from his savings account to his checking account. He walks up to the ATM, inserts his card, and presses the buttons to initiate the transfer. Behind the scenes, this transfer can be accomplished two ways. The first way is that the balance of Joe’s savings account can be reduced by $100 and then his checking account balance increased by $100. The second option is that his checking account balance can be increased and then his savings account balance decreased. But what happens if there’s a system crash between the two updates? Under the first scenario, Joe isn’t happy. He’s lost $100. Under the second example, Joe is very happy. He’s $100 richer, but the bank has now lost $100. What we want is that, in the event of the aforementioned crash, either both accounts must be updated or neither of the accounts updated. To ensure that this happens is the purpose of transactions.

    Transactions should follow the ACID rule—that is Atomicity, Consistency, Isolation, and Durability. Atomicity means that either all or none of the update is committed. Consistency means that if the transaction fails, the data is returned to the same state as before the transaction started. Isolation means that one transaction doesn’t know what another transaction is doing. Finally, Durability means that the transaction state is kept, no matter what happens to the system. This is generally handled through the use of a transaction log.

    We can implement transactions on our VFP data by using the BEGIN TRANSACTION/END TRANSACTION/ROLLBACK commands. This pseudo-code shows the preceding example:

      
    

    The transactions in VFP only work with VFP data, and VFP transactions fail the Durability rule of ACID. There’s no transaction logging.

    If we’re using SQL Server, we need to use a different mechanism. We can use ADO commands to tell SQL Server when to begin and end a transaction or we can code it in a stored procedure. However, what happens if the savings account is in SQL Server and the checking account data is in Oracle? SQL Server transactions will only work on SQL Server, and Oracle transactions will only work on Oracle. We need a single transaction that will work against both databases. That’s where the Distributed Transaction Coordinator (DTC) comes in.

    Using the DTC

    The DTC allows us to have transactions that cross databases. This means that we can have data in both SQL Server and Oracle that will be updated by the same transaction. Under MTS and VFP 6, Fox data couldn’t participate in a DTS transaction. (This changes under COM+ and VFP7. More on that later.) Back to the DTC.

    The DTS uses a two-phase commit. Basically, in phase one, DTS asks the database if it can do an update. If all of the databases respond yes to this question, then phase two begins, in which DTS tells the database to update the data. If any of the databases respond with a no, then DTS tells all of the databases to roll back the update.

    Let’s look at how transactions were handled under MTS:

      
    

    Note that when we either commit or abort the transaction, MTS will also release the component. This means that if the very next command in our application needs the component, we have to do another CREATEOBJECT() on the client. Let’s see how this changes in COM+:

      
    

    In this example, we can either commit or abort the transaction with SetMyTransactionVote, but still keep the instance of the component active until we explicitly call SetDeactivateOnReturn with a .T. parameter.

    You might now be wondering where the DTC fits in. COM+ automatically calls the DTC for us. The DTC talks to the database through the use of a Resource Manager. MTS and COM+ ship with Resource Managers for Oracle and SQL Server, but not for VFP. That’s why VFP data couldn’t take part in MTS transactions.

    It turns out that Resource Managers are very difficult to implement. So, COM+ has what are called Compensating Resource Managers (CRMs). CRMs are easier to implement than a Resource Manager. By using a CRM we can have our Fox data be involved in DTC transactions.

    A CRM consists of two parts, the CRM Worker and the CRM Compensator. These correspond to the two-phased commit of the DTC. A detailed discussion of the CRM is beyond the scope of this article. However, VFP 7 ships with a CRM sample application. You’ll find it in the SAMPLES\COM+\CRM folder under your VFP 7 installation.

    Configuring COM+ transactions

    Like many of the features in COM+, transactions are managed at runtime. Open the Component Services Manager applet and drill down to one of your components. Right-click on the component and select Properties, and then select the Transactions tab. You’ll see five transaction settings (see Figure 1 and Table 1).

    Table 1. Transaction types supported for COM+ components.Expand table

    SettingDescription
    DisabledTransactions aren’t needed. Set this when you don’t want the extra overhead of a transaction—for example, a component that doesn’t update any data.
    Not SupportedPrevents a component from using a transaction, regardless of the transactional state of the calling component.
    SupportedThe component participates in the transaction if one is active.
    RequiredThe component participates in the transaction if one is active. If there’s no active transaction, a new one is started.
    Requires NewA new transaction is always started.

    You’ll also notice the “Override global transaction timeout value” check box. When you select Required or Requires New transactions, this check box becomes enabled. You can then enter the number of seconds the transaction will run before timing out.

    That’s it for transactions. In Part 4, we’ll look at a COM+ feature that gives you asynchronous calls: Queued Components.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the July 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Decorating for the Busy Developer 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Decorating for the Busy Developer

    Lauren Clarke

    Decorating or “wrapping” an object represents a flexible alternative to subclassing. In this article, Lauren Clarke explains what a decorator is, demonstrates a novel way to implement the pattern in Visual FoxPro, and provides some application ideas.

    If you’re like me, your eyes tend to glaze over a bit when object-oriented design pattern terms are thrown about. The word “decorator” is one of those terms, and due in part to the image of Martha Stewart it immediately conjures up, and the complete lack of tasteful decorations in most programmers’ lairs, it leaves one wondering what decorations and programming have to do with each other. Decorator (a noun) is a design pattern that uses aggregation (another OOP term, which means one object holds a reference to another) to “wrap” an object and perhaps provide some additional features not present in the wrapped object. Simply put, a decorator is a class that provides a “skin” around another object to which one wishes to add new responsibilities at runtime. A simple example follows.

    Consider the object that SCATTER NAME creates in VFP:

      
    

    This line of code creates an object with properties that correspond to the fields of the table in the current workarea, and property values corresponding to the values of those fields. This process represents a nice, lightweight alternative to assigning the properties of a custom data object from the fields of a table. However, wouldn’t it be nice if loData could do something other than store field values? It’d be nice if it had methods you could use in your day-to-day life as a developer working with this object. For example, what if it could validate itself, or render itself as an XML string, or save itself? The trouble is that we don’t have access to the internal Visual FoxPro class used to create loData. We can’t create subclasses of it, and even if we could, we couldn’t force SCATTER NAME to use our subclass instead of the base class. So we have an object that we’d like to endow… decorate… with new abilities, and we can’t (or don’t want to) create a subclass to deal with these new responsibilities. So, we’re going to implement a method of extending the abilities of the SCATTER NAME object at runtime. Enter the decorator pattern.

      
    

    Well, that was a bunch of work (but don’t worry, I’m going to show you a way to avoid most of it). Let’s go over the highlights of this class. First, note that we have a property for each field in our customer table. Also, we have a property oData that will hold a reference to our SCATTER NAME generated data object. Finally, we have a property cFullName that will provide the customer’s full name based on the cFname and cLname fields. Then, we have a bevy of access and assign methods that serve to link the oData properties to the decorator’s properties. With this construct in place, the following expressions will both return the same value:

      
    

    The access and assign approach ties us to version 6.0 or later of VFP. The alternative is to use GetProp() and SetProp() methods to provide access to the wrapped object. For example, we could have a GetFname() function that would return oData.cFname, but this really makes the wrapper less than transparent, which isn’t desirable. Finally, note that the INIT() method takes as a parameter a reference to the data object to be wrapped by the class. Okay, let’s put our decorator to work.

    We’ll dissect the following code snippet line by line:

      
    

    First, we create a standard SCATTER NAME object. The BLANK keyword indicates the object will be created with empty properties.

      
    

    Next, we assign our new object a first and last name.

      
    

    Nothing spectacular so far, but this next line is the point at which the rabbit goes into the hat.

      
    

    Here, we instantiate a class DecoCust1, the init function of this class takes an object as a parameter, and we send our SCATTER NAME generated loData object for this parameter. DecoCust1 wraps itself around loData and augments its abilities. Note that we reuse the loData variable name to store a pointer to our new instance of DecoCust1. This isn’t necessary, but since DecoCust1 will seamlessly wrap our data object, presenting all of its original properties as if it were the old loData, it’s natural to do so. In our case, DecoCust1 has an IsValid() method, and we can use this to validate Mr. Yellow.

      
    

    If we pass this test, a record is added to the current table and we fire the Save() method, which will save the record to the table.

      
    

    Now that the object has been created and is decorated with additional properties and methods, we can check our record with IsValid(), save it with Save(), render it to XML with toXML(), and all the while we can still reference the objects’ original properties:

      
    

    And we also have an additional property calculated from underlying fields.

      
    

    To summarize, we have a class DecoCust1, which adds a cFullName property, IsValid(), Save(), and toXML() methods (and potentially many other useful methods) to our SCATTER NAME generated data object. From the developer’s standpoint, there’s very little practical difference between the wrapper and a bona fide subclass of the Visual FoxPro data object class. The key point, of course, is that you can’t subclass Visual FoxPro’s data object class. Also note that these abilities were added at runtime, meaning we have the option of adding these abilities if and when they’re needed without instantiating a large feature-ridden class each time a record object is needed.

    Another real-world sample of a decorator, csZIP/csUnZip, is available in the accompanying Download file. It decorates the popular DynaZip utility to provide some simplifications and extensions that are helpful when using the utility from within VFP.

    Subclasses vs. decorators

    Decorators offer an alternative to subclassing, but they must be used judiciously. The advantages come from the fact that you can gain subclass-like behavior for classes that you can’t actually subclass. Also, you’re given more flexibility at runtime to endow your objects with abilities only if they need it to fulfill the current task. In our example, there might be many places in an application where the base FoxPro data object will suffice. This being the case, it would be a shame to have to instantiate and use a complicated and expensive class where the lightweight class would do. Decorators offer “pay as you go” options where one can add functionality as needed. It’s possible to nest decorators inside decorators. If we decided that mixing the validation code and the XML rendering code in one class made it too large and inflexible, we could create a separate decorator for each task. For example, we could start with the basic data object:

      
    

    Then, if necessary, endow it with the ability to validate its properties:

      
    

    Then, if needed, we could decorate again and gain some rendering functionality:

      
    

    Which would allow us to do things like this:

      
    

    or this:

      
    

    And, unless you need to validate and render your data object every time you use it, you can save a lot of overhead by avoiding the giant do-everything data class.

    The following lists summarize the pros and cons.

    Advantages of decorators:

    1. They allow extension of classes that we can’t directly subclass.
    2. They allow us to avoid deep class hierarchies.
    3. They provide a pay-as-you-go option, which avoids instantiating large feature-ridden classes when few of the features are needed.

    Disadvantages of decorators:

    1. Pass-through code must be maintained.
    2. Passing requests through to the decorated component requires a performance hit.
    3. They can complicate debugging.

    From the developer’s perspective, the single most important issue here is the maintenance of pass-through code. The access and assign code must be maintained in concert with the wrapped object. In our example, this means that each time the structure of the table changes, we’ve got some work to do in our decorator class definition. This issue is exacerbated when we’re wrapping classes that have methods as well as properties, as we have to write pass-through code for each method. In short, if the interface of the wrapped object changes, so must the wrapper. Until recently, this fact was enough to really cool one’s feet to the idea of using the decorator in anything but the most dire situations. However, version 6.0 of Visual FoxPro gives us an opportunity to generalize decorator classes and completely eliminate this fragile use-case specific pass-through code. Our rescue comes in the form of the THIS_ACCESS method.

    THIS_ACCESS overview

    THIS_ACCESS is a method that can be added to any subclass in VFP. This method will fire every time the class is accessed. This means that every time a property is set or accessed or a method is called, the THIS_ACCESS method will fire prior to that action taking place. THIS_ACCESS takes as a parameter the name of the member being accessed. (Side note: It’s too bad that THIS_ACCESS only takes the called member as a parameter; if one could also access the value being sent [in the case of a property assignment] or the parameters being sent [in the case of a method call] inside the THIS_ACCESS method, it would open a world of possibilities, but that’s off topic for this article.) THIS_ACCESS must also return a reference to the object being accessed. It’s this last requirement that we leverage to implement an almost codeless delegation scheme for a generic decorator class. Let’s redo our prior example using this new approach.

    Redecorating with THIS_ACCESS

    Here’s what the class definition for DecoCust might look like when we utilize THIS_ACCESS:

      
    

    That’s it. Notice the substantial reduction in lines of code from our previous DecoCust1 example. The “big idea” here is the THIS_ACCESS method that first checks to see whether the requested member belongs to the decorator class, and, if not, a reference to the wrapped data object is returned. This way, the decorator can decorate by adding functionality like this IsValid() while forwarding requests for the oData properties directly to the oData object.

    Also, notice that the DecoCust2 class is very generic. The IsValid() and toXML() methods could be removed and we’d have a nice BaseDeco class to wrap any component that we could subclass to add things like IsValid() for specific implementations.

    Paying the piper

    Wrapping a class has some costs in terms of both development and runtime. The development costs come from the need to keep the interface of the wrapper synchronized with the interface of the wrapped component. If you choose to manually maintain the interface, this can be a costly proposition—especially if the wrapped class is changing often. Using the aforementioned THIS_ACCESS trick can vastly reduce your development load, as the interface will be updated automatically. However, since THIS_ACCESS fires each time the object is used, there’s a runtime cost to be paid for this approach. Table 1 will give you an idea of the runtime costs for these different approaches.

    Table 1. The different approaches and their associated runtime costs.Expand table

    TaskSubclassDeco1Deco2
    Instantiation11.812.66
    Access decorator property10.959.90
    Assign decorator property12.2011.34
    Call decorator method10.964.48
    Access decorated property10.966.81
    Assign decorated property11.145.37
    Call decorated method12.154.88
    Key:
    • Subclass = no decorator, subclass only.
    • Deco1 = a hardwired decorator with explicit pass-through code.
    • Deco2 = a decorator implemented with THIS_ACCESS.

    This table has been normalized to be machine-independent and more readable. For each task, the “Subclass” option has been given a weight of 1 and the others scaled accordingly. So, for example, Deco2 takes 9.90 times longer to access a property than a traditional subclass. To get actual time values for your system, just run the perfcheck.prg provided in the Download file.

    Some of these factors look pretty alarming, but keep in mind the times we’re talking about here. My system, a PIII that’s limping along at 500 Mhz, takes 0.000006 seconds to access a property from Deco1, and a staggering 0.00003 to access the same property through a decorator using THIS_ACCESS (Deco2). In a real use-case, say a middle-tier data object, an application might access a data object 100 times to serve a user’s request. In this situation, the THIS_ACCESS method represents a cost of no more than 0.003 seconds in our benchmark classes. Considering the THIS_ACCESS method might eliminate hundreds of lines of high-maintenance pass-through code, this might represent a good tradeoff. However, these results do make one pause to consider carefully where to implement these techniques.

    WITH/ENDWITH and GATHER gotchas

    If you plan on using a THIS_ACCESS decorated class in a WITH/ENDWITH loop, you’ll be in for a surprise. VFP exhibits some peculiar behavior in this area. In the April 2001 issue of FoxTalk, Randy Pearson wrote an article on the advantages of the WITH/ENDWITH command. It’s likely you’ll want to use this construct with a decorated class at some point. The trouble is that VFP won’t let you. The following code won’t fire the THIS_ACCESS method of loData and will result in an error:

      
    

    A workaround is to reference the decorated component directly in the WITH construct:

      
    

    There are some differences in the behavior here between VFP 6 and VFP 7 Beta 1. Both are odd and not really consistent with the documentation on access and assign methods. There’s a program in the Download file you can use to explore the differences.

    In addition to this, while the GATHER NAME command works fine with the property-level access methods, it seems to ignore the THIS_ACCESS method at this time.

    Conclusion

    The decorator pattern offers a nice alternative to subclassing. The THIS_ACCESS method of building decorators allows us to avoid writing reams of pass-through code when building decorators in VFP. This convenience comes with a performance price, but in many situations I think the price is more than justified. I’ll leave you with one possibly interesting diversion. Look up “multiple inheritance” in a good general OOP reference. Then, take a look at our DecoCust2 class, and consider the possibility of aggregating more than one object at a time and replacing the IF/THEN in the INIT() clause with a CASE statement. Bon voyage!

    (Lauren thanks the http://fox.wikis.com community for their help in refining and testing some of the ideas presented in this article.)

    Sidebar: References

    • Design Patterns, Elements of Object Oriented Software, by E. Gamma, R. Helm, R. Johnson, and J. Vlissides (Addison Wesley, 1994, ISBN 0201633612).
    • “Simulating Multiple Inheritance,” by Michael Malak, in the April 2001 issue of Journal of Object-Oriented Programming.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the August 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    TYPE() to StringType()–Bridging a Gap 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    TYPE() to StringType()—Bridging a Gap

    Pradip Acharya

    Intuitively, you’d think that the string “123” represents a number and the string “ABC” is of type character. Right? Not according to the standard TYPE() function in VFP. If ABC happens to be a date variable, for example, the type of “ABC” will be returned as D. Yet, in many situations, we need to know the data type of the contents of an unknown string. In this article, Pradip Acharya looks at the problems associated with determining the data type of a string and describes the creation of a new function, StringType(), suited for this purpose.

    One of my customers called and said, “When I type an X in your program, I get an error message, and nothing happens. Am I doing something wrong?” I rushed over and discovered that, as always, the customer was right, and I needed to create a new function to get the customer going again.

    A generic text input box was presented to the user for entering a search parameter for scanning a specific column in a table for the presence of such a value. For example, if the user needs to look at the invoices created on a certain date, a date will be typed in. To look for all five HP motors, one would enter a numeric value of “5” and so on. I had validation code that ensured that if the user entered data that clearly wasn’t of the same type as the field to be scanned, he or she would be notified and asked to re-enter the value. For a date type field, the user incorrectly entered an X. My validation, for some reason, failed to alert the user, and an unhandled data type mismatch error led to the previously noted disruption of work.

    Dual use of the TYPE() function

    Simply put, the TYPE() function in VFP accepts as input a character string and determines whether a memory variable or a field exists by that name and, if so, what its data type is. However, this description is deceptive and limiting. More strictly, TYPE() resolves the string as an expression and checks to see whether the result makes any sense.

    To make sure that the slate is clean:

      
    

    Now let’s define a memory variable:

      
    

    ?TYPE(“XYZ”) will display D. In this mode, the TYPE() function determined the data type of a prevailing memory variable (in scope). Next, if I do ?TYPE(“123”), I’ll get N. Obviously, in this second instance, my intention has been to use the TYPE() function to check the nature of a string and not the existence of a variable, field, object, or property.

    In trying to use the TYPE() function for the two unrelated purposes, we fall into a trap. In the case study presented earlier, if the user types 123, I’ll correctly determine that the type of the value entered is numeric. If, however, the user types XYZ instead of the desired data type C, I’ll incorrectly determine the data type to be D because a date variable already exists by the name of XYZ. This was the reason behind the failure of my validation code that then led to a data type mismatch error. The target table field was of type date; the user typed a character instead of a date, and I incorrectly inferred that a date had been typed in because a date variable by that name existed and TYPE() fooled me.

    To make matters worse, if the user enters a perfectly valid date such as 07/25/2001 (assuming that the date format is set to MDY), I’ll incorrectly determine that the data type of the string is N and not D. Why N? Because 07/25/2001 evaluates to 0.0001399 as an expression, having interpreted each “/” as a division operator—unintuitive indeed! What this proves is that using the TYPE() function to determine the data type of the content of a string is a misapplication of the TYPE() function. I created a new function, StringType(), to determine the type of data contained within a string, based on a string parsing strategy, and at the same time to internally take advantage of the TYPE() function.

    Function StringType()

    The purpose of this function is to determine the type of data contained in a text string that’s passed as the only argument. Case is immaterial, and leading and trailing white space, not just spaces, are ignored (see Table 1). White space is defined in Listing 1. (Note that there’s no U for undefined category.)

    ****

    Table 1. The function returns a single-character uppercase letter.Expand table

    LLogical
    NNumeric
    IInteger
    YCurrency (for example, $53.27)
    DDate, either strict or conforming to the prevailing date format, but independent of SET CENTURY
    CNot one of the above, character, default

    ****

    Listing 1. Code for function StringType().

      
    

    This function can be useful in any situation where an unknown string is encountered and one needs to determine what kind data it contains prior to taking further action. In addition to Listing 1, the file STRTYPE.PRG is available in the Download file. Table 2 presents a comparison of the output.

    ****

    Table 2. Comparison of output.Expand table

    StringTYPE()StringType()
    “.T.”LL
    “.False.”UL
    “123.52”NN
    “123”NI
    “$53.68”UY
    “01/01/01”ND
    “//”UD
    “123abc”UC
    “m.aVarName”DependsC

    An empty string

    An empty string or a string made up of white space isn’t interpreted. The function returns “C.” You may reserve a word BLANK, for example, and ask the user to enter BLANK if a distinction is to be made between no value entered and an intended empty value. Then test for “BLANK” on return. This isn’t done inside the StringType() as supplied, although you might wish to incorporate such a test inside the code yourself and assign a special return character—perhaps E.

    Data type logical

    The function will return L if the input string is one of the following:Expand table

    .T..F..True..False.

    The inclusion of the last two is an extension.

    Data types numeric and integer

    Normally, the function will return “N” if the string contains a number. As an extension, it will return “I” if the value entered is truly an integer. “123” and “123.” will both return “I”. The distinction between “N” and “I” might be useful, for example, prior to SEEKing an integer field of Invoice numbers. Under special circumstances, strings containing character data might be incorrectly identified as numeric in the presence of embedded operators. See the “Limitation” section later in the article.

    Data type currency

    Since parsing is involved, we might as well make a special case out of numeric data when the leading character is a $ sign. For example, “$ 52.68” will return Y instead of N. Probably no one will use this option.

    Data type date

    Correctly determining a string to be of type date is a vexatious problem. The problem is split into two parts. What’s an empty date? And what’s a valid date? If the input string is enclosed in curly brackets—that is, {…}—the result returned is always “D” regardless of the validity of what lies inside the brackets. In keeping with VFP convention, an input string like “{abc}” will return a value of “D.”

    Only two character representations are recognized by StringType() as a blank date:

    • • //
    • • {}

    In-between white space is ignored. Therefore, /ss/ or { ss} will also return “D”.

    As for valid dates, internally, the TYPE() function is put to use. The problem is that VFP is highly forgiving in interpreting dates. For example, ?{4.7} will print as 04/07/2001, whereas, for our purposes, we’d like to interpret 4.7 as a numeric value and certainly not a date. Accordingly, reasonable parsing constraints have been introduced in StringType() before a string can be declared to be a date. For example, there must be two and only two identical separator characters, and the rest must be digits. Dates entered in the strict date format will also be correctly identified as date.

    Limitation—no check for operators

    In this version of StringType(), no attempt has been made to isolate an individual data item from an expression with embedded operators. “53.86” and “2+7” both will return N (or I). Should we interpret a string such as 27*3 as “C” or “N”? I don’t know. Furthermore, StringType() normally doesn’t depend on which work areas are open. Not checking for operators leaves a loophole in this regard. An input string such as “53 * Invoice.Total” will produce unpredictable output depending on whether Invoice.Total is a visible field or not. If you code a version that checks for operators and expressions and closes this loophole, I’ll be happy to get a copy.

    A wish

    TYPE() as it stands identifies a valid property of an object. If TYPE(“m.oMyObject.Size”) returns “U” or “O,” it’s not a valid property. Otherwise, the property exists. As a logical and consistent extension to this interpretation, it makes sense if TYPE() also identifies an unprotected method and returns, for example, “H” if a method by this name exists for the object. I believe this generalization will be useful.

    Conclusion

    The standard TYPE() function will return the data type of a variable but isn’t suitable for determining the data type of the contents of a string. The new function StringType() has been designed specifically for this purpose, with a few limitations. In a future article, I’ll present a utility for generic output formatting of any type of value into a printable character string.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the September 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Create Modern Interfaces with VFP 7 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Create Modern Interfaces with VFP 7

    Doug Hennig

    It seems that every new version of Microsoft Office changes user interface standards. Whether you like it or not, your users expect your applications to keep up with this ever-moving target. Fortunately, VFP 7 adds new features that make it easier to create interfaces similar to Office 2000. Doug Hennig explains.

    In addition to new language features (see my “Language Enhancements in VFP 7” series of articles in the January to June 2001 issues of FoxTalk), database events, support for COM+, Web Services, and a ton of other new features, VFP 7 provides some user interface improvements, including hot tracking and modern-looking toolbars and menus.

    Hot tracking

    Hot tracking means controls appear flat (rather than the three-dimensional appearance we’re used to) but change appearance as the mouse pointer moves over them. Most controls will then appear sunken (the way they normally appear with hot tracking off), except for check boxes, option buttons, and command buttons, which appear raised. For an example of hot tracking, look at the toolbars in Microsoft Office 2000 applications. As you can see in Figure 1, toolbar controls appear flat (for example, the command buttons have no outlines) until you move the mouse over them.

    Hot tracking is easy to turn on in VFP 7: Simply set the SpecialEffect property to 2 (for check boxes and option buttons, you also have to set Style to 1-Graphical). For control classes that might have to be used in earlier versions of VFP, you should set this property programmatically (such as in the Init method) rather than in the Property Window to prevent an error when the control is used in those versions. Here’s an example (taken from SFToolbarButton in SFBUTTON.VCX):

      
    

    clVFP7ORLATER is a constant defined in SFCTRLS.H, the include file for SFToolbarButton, as follows:

      
    

    Since version(5) was added in VFP 6, the type() test and use of evaluate() in this statement ensure that it will work even in VFP 5.

    You can create other types of effects with code in the new MouseEnter and MouseLeave events. For example, you can set This.FontBold = .T. in MouseEnter and This.FontBold = .F. in MouseLeave to make a control appear bolded when the mouse is over it. You can also change the foreground or background color, and do pretty much anything else you want in these events.

    SwitchboardButton in MYCLASSES.VCX is an example. It’s used as a button in “switchboard” forms, forms that provide quick access to the major functions of an application. In VFP 7, as the user moves the mouse pointer around the form, the SwitchboardButton object under the mouse is surrounded with a blue outline (see Figure 2 for an example). SwitchboardButton is actually a container class with an image and a label. Its BorderColor is set to 0, 0, 255 (blue) and its Init method sets the BorderWidth to 0 (it’s left at the default of 1 in the Property Window so you can see it in the Class or Form Designers). The MouseEnter event sets BorderWidth to 3 and MouseLeave sets it back to 0.

    In addition to the SpecialEffect property and MouseEnter and MouseLeave events, command buttons have a new VisualEffect property. This property, which is read-only at design time, allows you to programmatically control the raised or sunken appearance of the control at runtime. Although you won’t often use this, it’s handy when several buttons should change appearance as a group. We’ll see an example of that later.

    Although you can use hot tracking wherever you want, I personally don’t care for hot tracking except in controls in toolbars (none of the dialogs in Microsoft Office use hot tracking, for example). So, rather than setting SpecialEffect to 2 in my base classes (those in SFCTRLS.VCX), I’ll do it in specific subclasses that I use for toolbars.

    To see an example of hot tracking for different types of controls, run TESTHOTTRACKING.SCX and see what happens as you move the mouse over each control.

    Toolbars

    Like other “modern” applications, toolbars in VFP 7 now have a vertical bar at the left edge when docked to provide a visual anchor to grab to move or undock the toolbar (see Figure 1). Another improvement related to toolbars is the addition of a Style property to the Separator base class; setting this property to 1 makes a Separator appear as a vertical bar at runtime (at design time, Separators are still invisible, which is kind of annoying). As with hot tracking, you might want to set this property programmatically to prevent problems with earlier versions of VFP; I use the following code in the Init method of SFSeparator (in SFCTRLS.VCX):

      
    

    Figure 3 shows the same toolbar running in VFP 6 and 7. The VFP 7 version looks and acts like a toolbar in a more modern application.

    A new style of toolbar button showing up in more and more applications is the dual button/menu control. Figure 4 shows an example of such a button, taken from Internet Explorer 5.5. Clicking on the left part of the control (the button with the image) causes an action to occur, while clicking on the down arrow displays a drop-down menu of choices. Another place I’ve seen such a control used is in West Wind Technologies’ HTML Help Builder to open Help projects. Clicking on the button displays an Open File dialog, while clicking on the down arrow displays a “most recently used” (or MRU) list of files. The advantage of this control is that it doesn’t take up much screen real estate, yet it can have a large list of choices.

    SFBUTTON.VCX has a couple of classes used to create such a control. SFDropDownMenuTrigger is a subclass of SFToolbarButton that’s sized appropriately and displays a down arrow (Caption = “6,” FontName = “Webdings,” FontSize = 6). It also has assign methods on its FontName and FontSize properties so they aren’t inadvertently changed programmatically by something like SetAll(). SFDropDownMenuButton is based on SFContainer, our container base class in SFCTRLS.VCX, and it contains an SFToolbarButton object named cmdMain and an SFDropDownMenuTrigger object named cmdMenu. The MouseEnter and MouseLeave events of each button set the VisualEffect property of the other button to 1 and 0, respectively, so the buttons’ hot tracking are synchronized. The Click event of cmdMain calls the ButtonClicked method of the container, which is empty since this is an abstract class and the desired behavior must be coded in a subclass or instance. The MouseDown event of cmdMenu has the following code to display the drop-down menu:

      
    

    Since SFContainer already has methods and code for handling shortcut menus (see my column in the February 1999 issue of FoxTalk, “A Last Look at the FFC”), why reinvent the wheel? As a refresher, the ShowMenu method of SFContainer instantiates an SFShortcutMenu object (defined in SFMENU.VCX), which is an adaptation (not subclass) of the FFC _ShortcutMenu class. SFShortcutMenu handles all of the work of displaying a shortcut menu; you just call the AddMenuBar and AddMenuSeparator methods to define the bars in the menu, and then call the ShowMenu method to display it. SFContainer.ShowMenu calls the ShortcutMenu method to do the actual work of defining the bars (that method is abstract in SFContainer).

    However, one issue SFDropDownMenuButton has to address that SFContainer doesn’t is menu placement. SFShortcutMenu automatically places the menu at the current mouse position, but if you look at Figure 4, you’ll notice the menu appears directly below the control, aligned with its left edge. To support that, I added nRow and nCol properties to SFShortcutMenu so you can control the position of the menu; if they contain 0, which they do by default, SFShortcutMenu will figure out where the menu should go, so the former behavior is maintained. The ShortcutMenu method of SFDropDownMenuButton, however, has to place the menu at the right spot, so it calculates the appropriate values for the nRow and nCol properties.

    What’s the right spot? That depends on if and where the toolbar hosting the control is docked. If the toolbar is docked at the right or bottom edges, the menu has to be placed to the left or above the control so it appears inside the VFP window. Otherwise, it has to be placed below and at the left edge of the control. The code to perform these calculations is fairly long and complex (I adapted—okay, ripped off <g>—the code from NEWTBARS.VCX in the SOLUTION\SEDONA subdirectory of the VFP samples directory), so it isn’t shown here.

    To use SFDropDownMenuButton, drop it or a subclass on a toolbar. To see an example, look at the instance named ColorPicker in the MyToolbar class in MYCLASSES.VCX, included in the Download file. ColorPicker is just a simple demonstration of this control; it allows the user to change the background color of the active form from either a pre-selected list of colors (the drop-down menu) or a color dialog (when you click on the button). The ButtonClicked method, called when the user clicks the button, displays a color dialog and sets the background color of the active form to the selected color:

      
    

    The ShortcutMenu method has the following code:

      
    

    toMenu is a reference to the SFShortcutMenu object. The first parameter for the AddMenuBar method is the prompt for the bar, and the second is the command to execute when that bar is chosen.

    Modern applications usually provide many different ways to perform the same action: main menu selections, toolbar buttons, shortcut menu selections, and so on. I’ve already discussed toolbars, and the SFShortcutMenu class makes it easy to create shortcut menus for every form and object in your application. So, let’s talk about the main menu.

    Menus haven’t changed much in FoxPro since FoxPro 2.0 (although in my August 2001 column, “Objectify Your Menus,” I presented a set of classes that make it easy to create object-oriented menus). New in VFP 7, however, are the abilities to specify pictures for bars (either the picture for a VFP system menu bar or a graphic file) and to create inverted bars that only appear when the user clicks on a chevron at the bottom of a menu popup (“MRU” menus, although the meaning of MRU here is different from how I used it earlier). These features allow us to create Office 2000-style menus.

    Specifying a picture is easy. In the VFP Menu Designer, click on the button in the Options column for a menu bar, and in the Prompt Options dialog, select File if you want to specify a graphic file or Resource if you want to use the picture for a VFP system menu bar. If you select File, you can either enter the name of the file in the picture text box or click on the button beside the text box and select it from the Open File dialog. If you chose Resource, either enter the name of the VFP system menu bar (for example, “_mfi_open”) or click on the button and select it from the dialog showing the prompts of system menu bars. In either case, a preview of the picture is shown in the Prompt Options dialog. The settings result in the PICTURE or PICTRES clauses being added to the DEFINE BAR command that will ultimately be created for this bar. If you’re using the OOP menus I presented in August, set either the cPictureFile or cPictureResource property of an SFBar object to the desired value.

    The MRU feature is more difficult to use, and much more difficult to implement in a practical manner. The DEFINE BAR command has new MRU and INVERT clauses, but because there are no specific options for either clause in the Menu Designer, you end up having to use a trick: Enter “.F.” followed by either “MRU” or “INVERT” in the Skip For option for the bar. VFP 7’s menu generator, GENMENU.PRG, is smart enough to see that you’re really using the Skip For setting as a way of sneaking other clauses into the DEFINE BAR command that the generator will create, so it leaves off the SKIP FOR .F. part of the command.

    However, that’s only the beginning. You’re responsible for managing what happens when the user selects the MRU bar (the chevron at the bottom of the menu) yourself. Typically, you’ll remove the MRU bar from the menu and add bars with the INVERT clause to the menu, but since the Menu Designer doesn’t create those bars for you, you have to code the DEFINE BAR statements yourself (although you could create the desired bar in the Menu Designer, generate the MPR file, copy the DEFINE BAR statement for the bar from the MPR, and then remove it in the Menu Designer). Also, once the user has selected one of the inverted bars, you have to add the MRU bar back to the menu and remove the inverted bars, except perhaps the selected one, which you may decide to leave in the menu as Office applications do. But then you have the complication of changing it from an inverted bar to a normal one and not adding that bar the next time the user selects the MRU bar, and that’ll only last until the user exits the application. See what I mean by “much more difficult to implement in a practical manner”?

    I can’t think of any application I’ve written in the past 20 years that was complex enough to actually use this type of MRU feature, but at least the OOP menu classes I presented in August manage a lot of this stuff for you. Set the lMRU property of an SFPad object to .T. if that pad should have an MRU bar in it, and set the lInvert property of any SFBar object to .T. to have that bar appear when the MRU bar is selected and disappear after a menu selection is made. You’ll have to subclass SFPad if you want different behavior, such as changing an inverted bar into a normal one if it’s selected.

    A more useful version of an MRU feature is the one I referred to earlier—a list of things the user has accessed recently. Office 2000 applications use this: The bottom of the File menu shows a list of the most recently accessed documents. Most VFP applications don’t use the concept of “documents,” but they do use records. It might make sense in some applications to put the most recently accessed records at the bottom of a menu so users can quickly return to a record they were working with before. Rather than automatically doing that, you might want to provide a function the user can select to add the current record to the MRU list.

    The sample application included in the Download file has an example of such a feature. First, a button in the MyToolbar class, used as the toolbar for the customers form, allows the user to “bookmark” the current record; it does so by calling the Bookmark method of the active form. That method in CUSTOMERS.SCX has the following code:

      
    

    This code expects that the Bookmark class, which we’ll look at in a moment, has been instantiated into a global variable called oBookmark. The AddBookmark method of that class expects two parameters: the command to execute when the bookmark is selected and the caption for the bookmark. In this case, the command tells VFP that if the active form is the customers form, call the Seek method of that form with the customer’s CUST_ID value (that method positions the form to the specified key value); if there’s no active form or it isn’t the customers form, call the DoForm method of the application object, telling it to run the customers form and passing the CUST_ID value (the Init method of the customers form accepts an optional CUST_ID value and calls the Seek method if it’s passed). The company name is used as the caption for the bookmark.

    The Bookmark class, in MYCLASSES.VCX, is a simple class based on SFCustom. It has a two-dimensional array called aBookmarks to store the bookmarks; the first column is the command to execute and the second is the caption. The nMaxBookmarks property determines how many bookmarks can be stored. The AddBookmark method adds a bookmark to the array and to the bottom of the File menu. Here’s the code:

      
    

    Before the first bookmark is added to the File menu, a separator bar is added above the Exit bar. Then bars for the bookmarks are added above that separator. The cBarPosition property is used to control the bar positions.

    Figure 5 shows an example of the File menu after I bookmarked four records. Selecting a bookmark opens the customers form (if necessary) and displays the chosen record.

    The Bookmark class has a couple of other methods, SaveBookmarks and RestoreBookmarks, that save and restore the bookmarks, using BOOKMARKS.DBF. These methods ensure that the user’s bookmarks are persistent between application sessions.

    Tying it all together

    The sample application shows all of the techniques discussed in this article. DO MAIN starts the application. MAIN.PRG instantiates some objects, including a simple application object and the Bookmark class, and creates a menu for the application. It then runs the SWITCHBOARD form and issues a READ EVENTS. The only functions in the menu and switchboard that do anything are Customers (which runs CUSTOMERS.SCX) and Exit.

    The switchboard form uses the SwitchboardButton class mentioned earlier to show hot tracking. The menu shows the use of MRU and inverted bars, includes pictures for some bars, and demonstrates the use of most recently used (bookmarked) records. The customers form isn’t fancy, but the toolbar it uses shows the SFDropDownMenuButton class (as a color picker), includes a button to bookmark the current record, and demonstrates the new features of VFP 7 toolbars, including buttons with hot tracking and vertical separator bars.

    VFP 7 has several new features that make it easier to create applications that look and act like Office 2000. Of course, Office XP raises the bar yet again, but for now, our applications can look more modern than VFP 6 applications could.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the October 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Become the Master of All You Survey–Using XML as a Flexible Data Capture and Retrieval Medium 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Become the Master of All You Survey–Using XML as a Flexible Data Capture and Retrieval Medium

    Andrew Coates

    Customer Relationship Management (CRM) systems often require that a survey or script be played for a particular customer while an agent is on the phone with them. Often, the exact content of the survey or script depends on the type of customer. In this article, Andrew Coates develops a system for capturing and retrieving such data. This technique uses XML as the storage medium for the answers to the questions and displays the enormous flexibility afforded by the use of a standard that is at once both free-form and structured.

    I recently was asked to retrofit an existing Customer Relationship Management system with the ability to capture information that my client wanted to gather from a targeted group of his customers via a telephone survey. My first thought was to add a new table to the database with a one-to-one relationship to the main table and a field for each question in the survey, as shown in Figure 1. This was probably the simplest approach, but on further reflection I realized that it lacks flexibility should my client ever want to undertake additional surveys.

    My next thought was to still have a table specific to the survey, but to have a one-to-many table allowing the entity to be linked to many survey tables. This would add some flexibility to the system in that new surveys could be added by adding a new table and adding rows to a survey master table. Information about which table referred to which survey could be stored in a survey master table, and information about which companies were eligible for which survey could be stored in a many-to-many table. This approach is illustrated in Figure 2.

    The downside to the second approach is that every time a new survey is added, a new table has to be added to the database. In addition, if a survey needs fine-tuning by adding or changing questions, then the data structure needs to be updated. While these aren’t insurmountable problems, I’d rather not have to muck around with the data structure every time my client wants to tweak the survey.

    What I needed was a flexible way of storing answers to survey questions that could still be retrieved in a structured way. I decided to store the answers directly in a text (or memo) field in the many-to-many table. To make sure that the data was still retrievable in a sensible manner, I decided to use XML as the storage format within the text field. The final layout of my database is shown in Figure 3.

    Data—the object of my desire

    The architecture I decided on for this project was one of data objects. Each set of answers to a survey is represented by a data object that has a property for each answer. The object is then converted to and from an XML representation using Rick Strahl’s ObjectToXML and XMLToObject from his (free) wwXML library. I’ve included the version of wwXML (with permission—thanks, Rick) that was current at the time of this writing in the accompanying Download file, but I strongly suggest that you visit Rick’s site (www.west-wind.com) to check whether there’s a more recent version. Rick’s constantly adding great stuff both to existing libraries and as completely new sets of useful things.

    As a companion to the data objects, there’s a GUI class. This class instantiates the appropriate data object and then the data object’s properties as control sources for its various controls. The GUI class itself does no data manipulation or handling (apart from displaying the data and allowing the user to enter or change answers). Instead, it calls the methods of the data object, which knows how to load and save itself from and to the XML data on the back end.

    Data object abstract class

    Both the data object and the GUI object are defined in the first instance as abstract classes—that is, classes that are only ever subclassed, never themselves instantiated. The data class standard properties and methods are shown in Table 1. ******

    Table 1. Methods and properties of the data object abstract class.Expand table

    Property/MethodDescription
    LoadData()Loads the XML data from the back end for the SurveyID/CompanyID combination specified in the properties.
    SaveData()Persists an XML representation of the object’s properties to the back end.
    CreateCursor()Creates a cursor with a field of the same name and of the appropriate type for each data property of the class.
    cCommentsStandard free text field available in all surveys.
    cConnectStringConnect string for use when instantiating the object and connecting to a remote back end.
    nCompanyIDID of the company to which this set of answers applies.
    nSurveyIDID of the survey from which this set of questions is taken.
    tLastUpdatedLast date/time this survey was saved for this CompanyID/SurveyID combination. Note that the default value is an empty time. You can’t just use {} for this, as this is interpreted as an empty date and the parser chokes on time values later on. You need to convert the empty date into an empty time like this: =DTOT({}).

    The code for the Init() method is shown in Listing 1.

    ****

    Listing 1. The data object’s Init() method.

      
    

    When the object is instantiated, the Init code accepts two parameters. The first parameter is an integer representing a handle of an already established connection to a back-end data source via SQLCONNECT() or SQLSTRINGCONNECT(). This gives the object an opportunity to share a connection and thus reduce the resource requirements of the application. The second parameter (which is only used if the connection handle passed is non-numeric or <= 0) allows the overriding of the SQL connect string used to establish a connection to the back end if there’s no handle passed.

    If there’s no connection handle passed, the data object’s first job is to establish a connection to the back end. If it can’t do this, there’s no use continuing and it bails out. Next it attempts to create an instance of the wwXML helper class. This class is vital for loading and saving the data to the back end, so again, if it can’t create an instance, it just bails out.

    The grunt work of the object is done by the LoadData() and SaveData() methods, shown in Listing 2 and Listing 3, respectively.

    ****

    Listing 2. Loading data from the back end.

      
    

    ****

    Listing 3. Saving the answers back to the database.

      
    

    If the method is passed XML as a parameter, it just uses that to load the values into the properties of the object (we’ll get to why this is useful a little later on). To load data from the back end, the method first checks that the company is listed for this survey. It then generates a SQL statement to retrieve any currently stored data for this company/survey combination. Finally, if there were already answers stored in the XML field, then the values are transferred to the data object’s properties with the single line:

      
    

    This is the powerhouse of the method. This single line of code transfers each property’s stored value from the XML. There are a couple of cool things to note here:

    • If the property’s been changed or added since the data was persisted to the table, the default value is used—there’s no requirement to go back through the data retrofitting changes to the survey. (Of course, it’s possible you may want to do some translations for business reasons, but the architecture we’ve used means that it’s not required from a technical standpoint.)
    • Deleting a property from the data object means that the data for that property simply won’t appear in the new version of the object, and next time the object is persisted to the table, the deleted data will simply disappear.

    Saving the properties in a data object is just as simple as loading them. Again the powerhouse of the method is a single line:

      
    

    This takes the properties in the current object and converts them to an XML representation. A sample of the XML generated by this call is shown in Listing 4.

    ****

    Listing 4. Survey answers stored in XML format.

      
    

    The rest of the method simply writes the XML string into the text field. Note that I’ve used a parameterized update command:

      
    

    The ?lcXML tells the ODBC connection to ask VFP for the value of the lcXML variable. Using this construct eliminates a large number of issues with encoding of illegal characters and so on. All of that is handled behind the scenes by the ODBC driver interface. Similarly, ?ltUpdateTime tells ODBC to ask VFP for the value of the ltUpdateTime variable. Dates are another great source of incompatibility between various back-end data sources. Letting the ODBC driver do the conversion and formatting in this way eliminates a potential headache if you change back ends.

    Adding a new survey

    The steps for adding a new survey are:

    1. Decide what questions are to be asked (or, perhaps more accurately, which answers will be recorded) and what type their answers will be.
    2. Subclass the surveybasedata class and add a property initialized to a value of the appropriate type for each answer. Override the CreateCursor() method to create a cursor with columns with the same names as the properties just added (don’t forget to add the four properties from the data base class—nSurveyID, nCompanyID, cComments, and tLastUpdated).
    3. Update the nSurveyID property for the data object subclass so this survey has a unique number.
    4. Create a subclass of the surveybase class that will present the survey to the user.
    5. Add a control or controls to the subclassed form with controlsources in the form thisform.oDataObject.<data object property>
    6. If there are any dependencies between the questions (for example, questions that should only be asked if others were answered in a certain way), then put code in the UpdateControls() method to change control states. Note that this method is called after the data object’s LoadData() method is called, so the control’s states are set initially. You should also call this method whenever there’s a potential need for a refresh. For example, if a check box state changes to true, another set of questions might become relevant.
    7. Update the cDataObjectName property with the name of the subclass you created in step 2. This ensures that the UI object instantiates the correct data object when it’s created.
    8. Make an entry in the Survey_Master table with the SurveyID from step 3, a description for the survey, the name of the data class from step 2, and the name of the UI class from step 4.
    9. For each company that’s eligible for a survey, add a row to the Company_Survey table with that company’s CompanyID and the SurveyID from step 3.

    While this might seem to be a lot of work, remember that you only need to do it once for each survey.

    Putting it into practice

    For the purposes of this exercise, I designed a simple survey with the questions shown in Table 2. ******

    Table 2. The survey questions.Expand table

    No.QuestionComments
    1How did you hear about our company?C(3) options are:
    WOM: Word of mouth
    YP: Yellow Pages
    WWW: Web search
    ADT: TV advertisement
    ADR: Radio advertisement
    OTH: Other
    1aIf Other—where did you hear about us?C(20)
    Only available if OTH selected for question 1
    2Gender?I
    1: Male
    2: Female
    3Do you use SQL Server?L
    4Do you use FoxPro (Visual or otherwise)?L
    5Do you use Visual Basic (not VBA or VBS)?L

    Next, I created a subclass of surveybasedata called surveycustomerdata and added properties for each of the questions. The subclass is included in the Download file, but the properties added were: cSource, cSourceOther, nSex, lSQLServer, lFoxPro, and lVB. I also overrode the CreateCursor() method to create a cursor with a field of matching name and data type for each property.

    I assigned this new survey an ID of 1.

    The next step was to create a subclass of the surveybase GUI class. I called this subclass surveycustomer.

    I added controls to the subclassed form—a drop-down for the source, a text box for the other source description, an option group for the gender, and check boxes for each of the development tool questions. These controls were in addition to those provided by the base class—companyID, last update date/time, and comments, as well as the Save and Cancel buttons.

    Because the other source text box should only be available if the user chooses Other from the source drop-down, I added enable/disable code to the UpdateControls() method that checks the value of the drop-down and takes appropriate action.

    I set the value of the cDataObjectName property to “surveycustomerdata” so the correct data object is instantiated by the GUI class.

    I updated the survey_master table by adding a row with the values SurveyID = 1, Survey_Name = “Generic Customer Survey,” Data_Class = “surveycustomerdata,” UI_Class = “surveycustomer.”

    Finally, I added two new rows to the Company_Survey table, one each for CompanyIDs 1 and 2, both with SurveyID 1.

    To display the survey, I typed the following in the command window:

      
    

    Note that the second and third parameters of the CreateObject() call are passed to the Init() method of the GUI object. In this case, the 1 means that I want to load the survey data for CompanyID 1, and the .t. means that I want to open the form in edit mode (rather than just viewing the data). The result is shown in Figure 4.

    Answering the questions and clicking the Save button fires the form class’s SaveSurveyData() method, which in turn fires the data object’s SaveData() method. The XML generated and saved to the memo field is shown in Listing 4.

    Retrieving the data

    “But wait,” I hear you cry. And you’re right. Storing data in this format doesn’t make querying and retrieval a simple matter of running a SQL statement. The data is stored in a free-form memo field, and most of us have had experience with how much of a hassle it is to retrieve data from there. This is where the power of the XML format (and one of Visual FoxPro’s most useful commands) comes to the fore. It’s a simple matter to retrieve all of the data from the text field into a cursor, and once it’s in the cursor the whole might of the native data engine is available to you.

    The code in Listing 5 (included as ExtractData.PRG in the Download file) shows how simple it is to retrieve all of the data from the XML memo fields into a single usable cursor.

    ****

    Listing 5. Retrieving VFP data from the memo field quagmire.

      
    

    After checking that the appropriate class library is loaded, the extraction program instantiates a data object and connects to the back end using the data object’s connect string. It then retrieves a cursor containing all of the rows with answers to survey questions. Next it calls the data object’s CreateCursor() method, which generates a cursor with a column of the appropriate type and name for each data object property.

    Scanning through the list of answer text fields, the code then passes the XML for each set of answers to the data object’s LoadData() method. Remember when I said that the tcXML parameter of the LoadData() method would come in handy? Well, here it is. It means that there’s no requirement to go back to the back end to retrieve the answers for each company. We can just get them all into a cursor with one back-end hit and then use the local data engine to scan through them and pass the XML to the data object.

    Once the data object has been loaded up with the answers, it’s time to add a row to the results table and populate it with the date. Two simple but very powerful lines of code do this:

      
    

    GATHER NAME is a wonderful command. It makes the process so much simpler. The alternative would be to iterate through all of the properties of the object and do a REPLACE for each one with the corresponding field in the cursor. I haven’t benchmarked this, but I imagine that having a native command to do this results in significant efficiencies.

    After scanning through the entire list of answers, this code will leave you with a cursor called ResultSet, which has a row for every company and a column for every answer. From there, the reporting process is up to you.

    Making changes to a survey

    The last thing I want to mention is how much flexibility this approach gives you. Let’s say that in our example you want to add a new question about the respondent’s income range and another about the number of computers at their primary working location. Let’s also suppose that you discovered the 30 characters you’d allocated to the other source field was too small—40 would be better—and that your boss is no longer interested in the answer to whether people are using VB. The following is all you’d have to do:

    1. Add a property called cIncomeRange and another called nComputerCount to the surveycustomerdata class.
    2. Remove the lVB property from the same class.
    3. Update the surveycustomerdata::CreateCursor() method to include cIncomeRange C(3) and nComputerCount I. Remove lVB and change the length of the cSourceOther field from 30 to 40.
    4. Update the surveycustomer class to include new controls for the income range (probably a drop-down like the source drop-down) and the number of computers (probably a spinner or text box). Ensure that their control sources are set to the matching properties of the data object. Remove the VB check box.

    That’s it! No data changes are required either to capture or to retrieve the data. The next time a survey is opened that was completed using the old format, the default values will be used for the new properties, the VB property will be ignored, and the existing 30 characters will be used for the other source field. As soon as it’s saved, the data will be in the new format.

    Extra credit

    You can use the metadata stored in the survey_master table to build a list of the surveys to which a particular company is “subscribed.” You could present a list to the user, including the description of the survey and the data that was last completed for this company in a GUI. The UI_Class field would then allow you to instantiate an appropriate form for viewing or editing survey responses.

    It’s quite simple to change the back-end database used for this class. In fact, I developed the system using a SQL Server back end. All you need to do is change the cConnectString property of the surveydatabase class to one that’s appropriate for your back end of choice and then set up the tables with the structure shown in Figure 3 on that back end. That’s it—the conversion’s complete.

    Conclusion

    Using XML to store data in a memo field provides an extremely flexible architecture while allowing the structured retrieval of the data. While it’s not essential that XML be used (it could have just as well been a proprietary text or even binary format in the memo fields), the fact that XML is a standard for this type of work means that tools like Rick Strahl’s wwXML library make working with the format simple and quick. I encourage you to get comfortable with this powerful data exchange format.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the November 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    Subclassing Existing Applications 

    • Article
    • 06/30/2006

    This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.

    Subclassing Existing Applications

    Richard David Hodder

    Most developers have a store of reusable code they rely on during development. Most (if not all) of our apps use or subclass things from this store. In this article, Richard David Hodder presents an approach for reusing application functionality across applications.

    You create subclasses to reuse existing functionality in a class and to extend/change its behavior. You can “subclass” an application to reuse existing functionality in the application and to extend/change its behavior. I use the term “subclassing” somewhat loosely here, but I think it conveys the right intent.

    Let’s say that I have an existing application “Oingo” and I wish to create a new application “Boingo” that’s based on Oingo. There are several possible reasons to subclass the Oingo application:

    • Add functionality: I want Boingo to have all of the functionality of Oingo plus some new features.
    • Substitute/override functionality: I want Boingo to have all of the functionality of Oingo with substitutions for some existing functionality (for example, an Import form that looks totally different).
    • Remove functionality: I want Boingo to have all of the functionality of Oingo with some existing functionality removed (for example, remove the Import option from the menu).

    The first approach that might come to mind would be to create a new project file for Boingo, within the Oingo application directory. This approach mixes code bases and leaves you at high risk for breaking Oingo while trying to extend behavior in Boingo.

    The most direct approach would be to create a new directory for Boingo, copy all of the files from the Oingo application into the new directory, make changes/add functionality, rebuild, test, and distribute, right? The problem with this approach is that the “connection” between Oingo and Boingo no longer exists: They no longer stem from the same code base. There are now two distinct code bases: When changes to Oingo are made, the changes must also be applied to Boingo to maintain the same functionality.

    In a perfect world, several things would happen:

    • The Boingo project would have its own directory.
    • The project file for Boingo would be pointing to the same code base that’s found in the Oingo project file. This would allow the changes to be made to Oingo and “ripple” to Boingo.
    • The project directory for Boingo should have markedly less code—only the code that implements the additions, substitutions, and removals of Oingo functionality.
    • Changes to the application layer (“a-layer”) code in Oingo would be inherited by Boingo.
    • Changes to the a-layer in Boingo wouldn’t be inherited by Oingo: Again I refer you to the subclass metaphor. Changes in subclasses don’t affect the superclass; there’s no “upward” inheritance.

    One of my favorite quotes is, “Wherever you go, there you are.” In a perfect world, you’d know the exact locations where functionality would need to be added, substituted, and removed before the originating application (Oingo in this example) is designed. This would allow you to do things like place hooks into and data-drive the application in order to control what’s in an application and how to extend it. Most times it’s not possible, and you may not know beforehand that the application needs to be “subclassed.” That’s why I included the word “existing” in the title of this article: You’ve already created the application. Now you want to make use of your existing work.

    The keys to subclassing an application

    The approach I’ll present later for subclassing an application relies on being able to “trick” VFP into using other functions and classes. I’ll present the general concepts first, and then I’ll demonstrate how I applied them in Codebook applications I’ve written.

    The keys to subclassing an application are:

    • Managing the program stack (SET PROCEDURE)
    • Managing the class stack (SET CLASSLIB)
    • Managing the pathing (SET PATH)
    • Copying the project file and massaging its contents
    • Copying supporting files

    ****

    Management of the program stack

    When you call a function, VFP goes through the list of PRGs in the SET PROCEDURE list until it finds the function and then executes it. It goes through the list in the order of the PRGs in the SET PROCEDURE list. If two functions with the same name exist in one or more of the PRGs, then VFP executes the first one it encounters. Most of us have made the mistake of accidentally creating two functions with the same name. It gets really fun (read: frustrating <g>) when you keep modifying the function that’s later in the stack, and then run the application and find that things still seem broken. It always happens at 3 a.m. when you’re tired and out of caffeine. Consider the following three programs (Test, Foo, and Bar):

    *-- TEST.PRG
    SET PROCEDURE TO FOO,BAR
    Hello()
    *-- FOO.PRG
    PROC Hello
    MESSAGEBOX("I'm broken")
    ENDPROC
    *-- BAR.PRG
    PROC Hello
    MESSAGEBOX("I'm fixed")
    ENDPROC
    
    

    When you run TEST.PRG, you’ll always get the message box saying “I’m broken,” no matter how many times you change the “I’m fixed” string in the Hello function in BAR.PRG. That’s because when walking through the list of PRGs in SET PROCEDURE, VFP finds the Hello function in FOO.PRG first. The same goes for instantiating classes stored in PRGs: The first class encountered in the SET PROCEDURE list is instantiated.

    You might be asking, “What’s that got to do with subclassing an existing app?” Consider the following situation: Oingo has a stock price importing form that calls a function named ImportStockQuotes, which retrieves stock quotes from a Web site and imports them into a database. You want Boingo to import stock quotes from a file on disk rather than from a Web site. To change just the ImportStockQuotes “behavior” in Boingo, add a PRG to the Boingo project (for example, BoingoFunctionOverrides.PRG) and in that PRG create a function called ImportStockQuotes and fill it with the code to import the quotes from a file.

    The only step left in order to override the ImportStockQuotes behavior is to make sure that Boingo loads BoingoFunctionOverrides.PRG into the SET PROCEDURE list earlier than Oingo’s ImportStockQuotes. If you want to change the behavior of a class called DataImporter stored in a PRG, add a PRG to the Boingo project (for example, BoingoClassOverrides.PRG). In that PRG, create a class called DataImporter and fill it with the code that performs the behavior you want that class to execute in Boingo. You can be surgically “precise” with what you change by using this approach.

    ****

    Management of the class stack

    Managing the class stack is very similar to managing the program stack. When you instantiate a class that exists in a VCX class library, VFP goes through the list of class libraries in the SET CLASSLIB list until it finds the class and then instantiates it. It goes through the list in the order of the class libraries in the SET CLASSLIB list. If two classes with the same name exist in one or more of the class libraries, then VFP instantiates the first one it encounters. There’s one important difference between managing the program and class stacks: The class stack can’t contain two class library files with the same name. For example, suppose you had two directories (Oingo and Boingo) and each had a class library called DeadMansParty.VCX. If you executed the following command:

    SET CLASSLIB TO ;
    \Oingo\DeadMansParty, ;
    \Boingo\DeadMansParty
    
    

    you’d get the cryptic error “Alias name is already in use.” Obviously, behind the scenes VFP is opening the class libraries as tables, and you can’t have two tables with the same alias (DeadMansParty) open in the same workarea. You could get around this by using the ALIAS parameter of the SET CLASSLIB command:

      
    

    As with the program stack, the trick to subclassing an application is to make sure that the a-layer class libraries for the subclassed app (Boingo) are loaded into SET CLASSLIB earlier than the class libraries from the superclass app (Oingo).

    ****

    Management of the pathing

    Managing the path by using SET PATH can also be important. Codebook developers can test their applications without having to build executables by taking advantage of path settings. Boingo may rely on the setting of path to open a file that’s in the Oingo directory. ******

    Copying the project file and massaging its contents

    A few paragraphs ago I mentioned that it’s important to make sure that Oingo and Boingo are based on the same code base. What’s the quickest way to do that? Just copy the Oingo project file (PJX and PJT) into the Boingo directory, right? Close but no cigar. Now Boingo’s project thinks that its directory contains Oingo’s code: The pathing to the project items is wrong. The pathing needs to be adjusted so that the items in the Boingo project file point to the items in Oingo’s directory. Some care has to be taken with this because some items still should exist in the Boingo directory—supporting files, for example. ******

    Copying supporting files

    Some items in a project support a framework or application and are application-specific, and therefore copies should be made of them, rather than pointing to the superclass app’s files. Databases and tables are a good example of this. What good is it to point the Boingo project at Oingo’s data, when one of the things that may be changing from Oingo to Boingo is data structures? Also, for development environment purposes it’s better if Boingo has its own data structures, even if there are no changes. Other examples of supporting files are reports and INI files. Support files will differ based upon which framework(s) you develop with (for instance, Codebook, INTL, WebConnect, and so forth).

    Frameworks can make most if not all of the keys to subclassing an application simpler to achieve because usually they’ve been addressed in the framework. For example, as you’ll soon see, Codebook keeps a table of the programs and class libraries to load. By changing the framework slightly, these programs and classes can be loaded in a manner that supports the task of a-layer ordering of items in SET CLASSLIB and SET PROCEDURE.

    Subclassing a Codebook application

    Rather than trying to address all possible frameworks and libraries, I’m going to stick to territory that’s familiar for me: the Codebook framework. ******

    Management of the program and class stacks in Codebook

    Codebook applications build a list of the programs and class libraries used by the application by querying the contents of the project file. In a function named BuildMetaData in Setup.PRG, the project file is opened with the alias “_project” and then the following query is run:

    SELECT NAME, TYPE ;
    FROM _project    ;
    WHERE !DELETED() AND ;
    (TYPE = "V" OR ;
    TYPE = "P")   ;
    ORDER BY TYPE    ;
    INTO CURSOR cTemp
    
    

    Records with a type equal to “V” are class libraries (VCXs), and records with a type equal to “P” are programs (PRGs). The contents of this cursor are saved to a table called METADATA, which is built into Codebook executables. In another program named SetPath, the information in this cursor is looped through. All of the program records (type=”P”) are used to build a comma-delimited list of the programs in the project. The function then SETs PROCEDURE to this list of files. All of the class library records (type=”V”) are used to build a comma-delimited list of the class libraries in the project. The code then SETs CLASSLIB to this list of files.

    Although this list is organized by type, this doesn’t order the files so that the subclassed application’s a-layer will get loaded earlier. Then I came up with an idea. I had the project file opened as a table, and I was looking at the Name field of the PJX file. The Name field uses relative pathing to point to project items. Therefore, framework files all started with “..” (for example, “..\common50\libs\capp.vcx”). Files that were in the a-layer, on the other hand, did not start with “..” because they were either in the project’s directory or a subdirectory of the project’s directory (for example, “progs\solvetheworldsproblems.prg”).

    I decided to change the contents of MetaData.DBF and reorder the records so that names not beginning with “..” float to the top of the list. Here’s the modified query that I used:

      
    

    I created a new field (LAYER) that holds the first two characters of the name of the file. This field is only used for the ORDER BY of this query. The framework doesn’t use the field for anything. All non-a-layer code will have a LAYER equal to “..” and all a-layer code will not. Due to the fact that the period character “.” has a lower ASCII value than the alphabetic characters, it was necessary to order the list by descending LAYER so that all a-layer records would be at the top of the cursor. Being at the top of the cursor, they get loaded into the respective stacks first!

    ****

    Management of the pathing in Codebook

    The main reason for managing pathing in Codebook is to create a development environment that allows you to develop an application without constantly having to rebuild the executable. When subclassing an application, Codebook must know two things: first, whether the current application is a subclassed application (like Boingo), and second, if it is, where the superclass application (Oingo) resides so that it can adjust its paths to point at Oingo. To solve this, I create a #DEFINE called APPLICATION_SUPERCLASS_DIRECTORY that contains the superclass’s directory. This #DEFINE only exists if the current application is a subclass. Therefore I was able to use the #IFDEF directive to change the pathing in the SetPath function in Setup.PRG:

    #IFDEF APPLICATION_SUPERCLASS_DIRECTORY
    LOCAL lcAppSubClassDir
    lcAppSubClassDir = ".."+ ;
    APPLICATION_SUPERCLASS_DIRECTORY + ;
    IIF(RIGHTC(APPLICATION_SUPERCLASS_DIRECTORY,1)!= ;
    "","","")
    lcPath = lcPath+ ","+;
    lcAppSubClassDir+"\PROGS, "+ ;
    lcAppSubClassDir+"\FORMS, "+ ;
    lcAppSubClassDir+"\LIBS, "+ ;
    lcAppSubClassDir+"\GRAPHICS"
    #ENDIF
    
    

    #IFDEF will only execute its code if the #DEFINE exists (if the application is a subclass).

    ****

    Copying supporting files and the project file and massaging its contents in Codebook

    I created a simple tool called the Codebook Application Subclasser (see Figure 1).

    The form SUBCLASS.SCX is available in the accompanying Download file. I won’t show all of the code here, but once the superclass application directory and subclass application directory are chosen, the following steps are taken:

      
    

    Let’s look at the last four function calls in this code snippet.

    BuildIncludeFile—This routine creates the application include file for the subclass application (Boingo). It #INCLUDEs the application include file from the superclass application (Oingo). This is done so that #DEFINEs added to Oingo in the future will be “inherited” by Boingo. Boingo’s include file can be added to manually: #DEFINEs added to this file don’t affect Oingo (as I said before, inheritance doesn’t move upward).

    ModifyMainProgram—Main.PRG is the main program for Codebook applications. Main.PRG gets copied over from the superclass. ModifyMainProgram uses LLFF (low-level file functions) to add the equivalent of the following to the bottom of Main.PRG:

      
    

    This has no effect on the code, but it makes sure that the next time the Boingo project is built, the aappBoingo class library will be added to the Boingo project.

    ModifyStartCB—StartCB.PRG is a program that sets up the development environment (paths and so on) for an application. It isn’t built into the application; it merely sets up the environment. ModifyStartCB adds code to StartCB so that the superclass application’s paths are included.

    CreateApplicationObjectSubclass—Every application created with the Codebook framework has an application object stored in AAPP.VCX. The application object holds global information for the application. When subclassing an application, it may be necessary to add or change properties and functionality on the subclassed application’s application object. For example, Boingo might need to attach a timer object to the application object to remind the user to import stock prices (a feature not available in Oingo). Therefore, I create a subclass of the superclass app’s application object and place it in the LIBS directory of the Boingo project’s directory. Boingo-specific application object changes get made to this object. In order to avoid the class library naming collision problem mentioned earlier, I name the class library “AAPP<Subdirectory of CDBK50 To Create>” (for example, AAPPBoingo).

    When to subclass an application

    Just because cause you can subclass an application doesn’t mean that you should. I can think of two occasions when subclassing an application would be appropriate:

    If you sell a product and receive a lot of requests for “one-off” functionality.

    If products will be internationally distributed. I worked on products that were developed in the U.S., but with the foresight to make them ready for the international market by incorporating Steven Black’s INTL Toolkit (www.StevenBlack.com). As international versions of the product were developed, there was locale-specific functionality that needed to be incorporated. Spain required new reports, Italy needed enhancements to the import form, and so on.

    Lessons learned

    Having coding standards—particularly frameworks and standard directory structures and the like—made this process simpler: This is true for coding in general. I’m not suggesting that you use Codebook specifically (although four out of five VFP developers surveyed… <g>). Rather, I suggest that you pick (or create) standards and stick with them.

    Also, use of the NEWOBJECT() function could actually get in your way because it hard-codes the location of the class library of the class you wish to instantiate. I’ve never really been a fan of this function for that very reason. I prefer to let the environment figure it out (particularly if I need to refactor classes into new class libraries), but that’s just my own bias.

    To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

    Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.

    This article is reproduced from the December 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.

    By Amjad Izhar
    Contact: amjad.izhar@gmail.com
    https://amjadizhar.blog

  • Microsoft Visual FoxPro 9.0

    Microsoft Visual FoxPro 9.0

    Learn Microsoft Visual FoxPro 9.0

    • Article
    • 08/29/2016

    Microsoft® Visual FoxPro® database development system is a powerful tool for quickly creating high-performance desktop, rich client, distributed client, client/server, and Web database applications. Employ its powerful data engine to manage large volumes of data, its object-oriented programming to reuse components across applications, its XML Web services features for distributed applications, and its built-in XML support to quickly manipulate data.

    Visual FoxPro 6 Language Reference Book on Archive.Org

    Note that Visual FoxPro 9.0 is the last version and was published in 2007.


    Download Visual FoxPro 9.0 SP2

    Download Service Pack 2 for Microsoft Visual FoxPro 9.0. SP2 provides the latest updates to Visual FoxPro 9.0 combining various enhancements and stability improvements into one integrated package.

    Three Hotfixes for Visual FoxPro 9.0 SP2

    Visual FoxPro Samples and Updates

    Find code samples and product updates for Visual FoxPro.

    Visual FoxPro on MSDN Forums

    Join the conversation and get your questions answered on the Visual FoxPro Forum on MSDN.

    Visual FoxPro 9.0 Overview

    With its local cursor engine, tight coupling between language and data, and powerful features, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications for the desktop, client-server environments, or the Web. Developers will have the necessary tools to manage data—from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully-developed data management application for end users.

    • Data-Handling and Interoperability. Create .NET compatible solutions with hierarchical XML and XML Web services. Exchange data with SQL Server through enhanced SQL language capabilities and newly supported data types.
    • Extensible Developer Productivity Tools. Enhance your user interfaces with dockable user forms, auto-anchoring of controls, and improved image support. Personalize the Properties Window with your favorite properties, custom editors, fonts, and color settings.
    • Flexibility to Build All Types of Database Solutions. Build and deploy stand-alone and remote applications for Windows based Tablet PCs. Create and access COM components and XML Web Services compatible with Microsoft .NET technology.
    • Reporting System Features. Extensible new output architecture provides precision control of report data output and formatting. Design with multiple detail banding, text rotation, and report chaining. Output reports supported include in XML, HTML, image formats, and customizable multi-page print preview window. Backward compatible with existing Visual FoxPro reports.

    Resources

    Visual FoxPro Downloads

    • Article
    • 08/29/2016

    Download samples, along with the final product updates including service packs for Visual FoxPro to ensure maximum productivity and performance from your Visual FoxPro development.

    Visual FoxPro 9.0 Updates

    • Visual FoxPro 9.0 Service Pack 2 (SP2)
      Download Service Pack 2 for Microsoft Visual FoxPro 9.0. SP2 provides the latest updates to Visual FoxPro 9.0 combining various enhancements and stability improvements into one integrated package.
    • Help Download for Visual FoxPro 9.0 SP2
      Download product documentation for Visual FoxPro 9.0 SP2.
    • GDI+ Update for Visual FoxPro 9.0 SP2
      Security update patch for Visual FoxPro 9.0 SP2 for fixing Buffer Overrun in JPEG Processing (GDI+).
    • GDI+ Update for Visual FoxPro 9.0 SP1
      Security update patch for Visual FoxPro 9.0 SP1 for fixing Buffer Overrun in JPEG Processing (GDI+). Note: We highly recommend that you install Service Pack 2, then apply the GDI+ SP2 update.
    • Visual FoxPro 9.0 ‘Sedna’ AddOns
      AddOn pack for Visual FoxPro 9.0. This download contains six components: VistaDialogs4COM, Upsizing Wizard, Data Explorer, NET4COM, MY for VFP and VS 2005 Extension for VFP.
    • XSource for Visual FoxPro 9.0 SP2
      Download XSource for Visual FoxPro 9.0 SP2. XSource.zip has its own license agreement for usage, modification, and distribution of the Xbase source files included.
    • Microsoft OLE DB Provider for Visual FoxPro 9.0 SP2
      The Visual FoxPro OLE DB Provider (VfpOleDB.dll) exposes OLE DB interfaces that you can use to access Visual FoxPro databases and tables from other programming languages and applications. The Visual FoxPro OLE DB Provider is supported by OLE DB System Components as provided by MDAC 2.6 or later. The requirements to run the Visual FoxPro OLE DB Provider are the same as for Visual FoxPro 9.0. Note: This version of the VFP OLE DB provider is the same version as the one included with Visual FoxPro 9.0 SP2.
    • VFPCOM Utility
      Extend Visual FoxPro interoperability with other COM and ADO components with the VFPCOM Utility. This utility is a COM server that provides additional functionality when you use ADO and access COM events with your Visual FoxPro 9.0 applications. For installation instructions and more details on the issues that have been addressed, consult the VFPCOM Utility readme.
    • Visual FoxPro ODBC Driver
      The VFPODBC driver is no longer supported. We strongly recommend using the Visual FoxPro OLE DB provider as a replacement. Please refer to the following article for more information and related links to issues when using the VFPODBC driver: https://support.microsoft.com/kb/277772.

    Visual FoxPro 8.0 Updates

    • Visual FoxPro 8.0 Service Pack 1Download Microsoft Visual FoxPro 8.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 8.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.
    • GDI+ Update for Visual FoxPro 8.0 SP1Security update patch for Visual FoxPro 8.0 SP1 for fixing Buffer Overrun in JPEG Processing (GDI+).
    • Visual FoxPro 8.0 SP1 Task Pane Source CodeSource code for Task Pane Manager component included in SP1 for Visual FoxPro 8.0. SP1 for VFP 8.0 included an updated Task Pane Manager component as an .APP application file but did not contain the update source code files associated with the updated version.
    • Visual FoxPro 8.0 Localization Toolkit OverviewOverview document of the Localization Toolkit project results for making available various language versions of the design-time IDE DLL and help documentation as add-ons to the English version of Visual FoxPro 8.0.

    Visual FoxPro 7.0 Updates

    • Visual FoxPro 7.0 Service Pack 1Download Microsoft Visual FoxPro 7.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 7.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.

    Code Samples

    • .NET Samples for Visual FoxPro DevelopersThis download contains different projects and source files which are designed to show how how some common Visual FoxPro functionally is created in Visual Basic .NET.
    • Visual FoxPro 8.0 SamplesThis download contains different projects which are designed to show how new features in Visual FoxPro 8.0 can be used. Each project is self-contained and can be run independently of any other. There is a readme text file contained in each project that describes each sample program.
    • Sample: Visual FoxPro DDEX Provider for Visual Studio 2005A Data Designer EXtension Provider allows a data source to integrate better with data tools in Visual Studio. Visual FoxPro “Sedna” included a sample for such a provider for VFP data.This is now available as a stand-alone download.

    System Requirements

    • Article
    • 08/29/2016

    To install Microsoft Visual FoxPro 9.0, you need:Expand table

    Minimum Requirements
    ProcessorPC with a Pentium-class processor
    Operating SystemMicrosoft Windows 2000 with Service Pack 3 or later operating systemMicrosoft Windows XP or laterMicrosoft Windows Server 2003 or later
    Memory64 MB of RAM minimum; 128 MB or higher recommended
    Hard Disk165 MB of available hard-disk space for typical installation; 20 MB of additional hard-disk space for Microsoft Visual FoxPro 9.0 Prerequisites
    DriveCD-ROM or DVD-ROM drive
    DisplaySuper VGA 800 X 600 or higher-resolution monitor with 256 colors
    MouseMicrosoft Mouse or compatible pointing device

    Frequently Asked Questions

    • Article
    • 08/29/2016

    Find answers to your frequently asked questions about Visual FoxPro.

    Q: What operating system is required for Visual FoxPro 9.0?

    Developing applications with Visual FoxPro 9.0 is supported only on Microsoft Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. You can create and distribute run-time applications for Windows 98, Windows Me, Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. Installation on Windows NT 4.0 Terminal Server Edition is not supported.

    Q: Will there be a Visual FoxPro 10.0?

    No. There will not be another major release of Visual FoxPro (see announcement: A message to the community, March 2007).

    Q: Will there be updates to Visual FoxPro?

    Yes. Visual FoxPro will continue to be supported as per the lifecyle policy (https://support.microsoft.com/lifecycle/?p1=7992). Visual FoxPro 9 will be supported until 2014. In support of these products we may release patch updates from time to time. These typically fix problems discovered either internally or by a customer and reported to our product support engineers.

    Q: Will there be a service pack 3 for Visual FoxPro 9?

    At this time there are no plans to release a service pack for Visual FoxPro. However if there arises a need to publish a collection of fixes we may release a service pack. We will make announcements on the Visual FoxPro home page.

    Q: What types of applications can I build with Visual FoxPro 9.0?

    With its local cursor engine, tight coupling between language and data, and powerful features, such as object-oriented programming, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes, from desktop and client/server database applications to data-intensive COM components and XML Web services.

    Visual FoxPro 9.0 is an application development tool for building extremely powerful database applications and components. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications on the desktop, client/server, or on the Web, through components and XML Web services. Developers will have the necessary tools to manage data from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully developed data management application for end users.

    Q: Can I use Visual FoxPro to build Web applications?

    Visual FoxPro COM components can be used with Internet Information Services (IIS) to build high-powered Internet database applications. This is because Visual FoxPro components can be called from Active Server Pages (ASP). Visual FoxPro is compatible with ASP but works even better in conjunction with the more modern ASP.NET. The components will retrieve and manipulate data, and will build some of the HTML returned to the user.

    Q: Can you consume XML Web services with Visual FoxPro?

    Yes, Visual FoxPro 9.0 makes it easy to consume XML Web services by integrating the SOAP Toolkit into the product.

    Q: Is Visual FoxPro a part of MSDN Subscriptions?

    Yes, Visual FoxPro 9.0 is included in the Professional, Enterprise, and Universal levels of MSDN Subscriptions. Visual FoxPro 9.0 is available for download to MSDN Subscribers via MSDN Subscriber downloads.

    Q: How long will Visual FoxPro be supported by Microsoft?

    Visual FoxPro 9.0 has standard support by Microsoft through January 2010 and extended support through January 2015 as per the developer tools lifecycle support policy.

    Q: How long will the SOAP Toolkit included in Visual FoxPro 9.0 be supported by Microsoft?

    Licensed users of Visual FoxPro 9.0 have a special lifecycle support plan for the SOAP Toolkit, supported by Microsoft on the same support plan as Visual FoxPro 8.0 which is through April 2008 and extended support through September 2013.

    Q: Is Visual FoxPro 9.0 compatible with Visual Studio 2005 and SQL Server 2005?

    Yes. We improved XML support and added new data types in Visual FoxPro 9.0 which improves .NET interop and SQL Server compatibility. Moreover the ‘Sedna’ add-on pack includes improvements to the Data Explorer and the Upsizing Wizard. These have significant improvements to support SQL Server 2005.

    Q: How does Visual FoxPro 9.0 compare to SQL Server?

    We do not contrast Visual FoxPro versus SQL Server. We position SQL Server as a database engine and Visual FoxPro as a developer tool. While Visual FoxPro has a database engine built-in, it is not positioned as a stand-alone database engine only. The trend is for an increasing amount of Visual FoxPro based applications to use SQL Server as the data storage in the solution. Of course, this is not required; it depends on the requirements of the solution. SQL Server offers security, reliability, replication, and many other features of a full relational database engine while the Visual FoxPro database system is an open file based DBF system that does not have many of those features. We leave it up to developers and companies to position and to compare various Microsoft products and technologies with each other and decide which ones are best for them to use when and how.

    Q: Are there plans to enhance the 2 GB database size limit in Visual FoxPro?

    The 2 GB limit is per table, not per database. We do not have any plans to extend the 2 GB table size limit in Visual FoxPro due to many reasons including the 32-bit architecture that already exists within the product. For large, scalable databases we recommend SQL Server 2008.

    Q: Is Visual FoxPro supported on Windows Vista?

    Yes. Visual FoxPro 9 Service Pack 2 is fully supported on Windows Vista.

    Q: Are there plans for Visual FoxPro to support 64-bit versions of the Windows operating system?

    No. While Visual FoxPro will remain 32-bit and not natively use 64-bit addressing; it will run in 32-bit compatibility mode. Visual Studio 2008 supports creating native 64-bit applications.

    Q: How do you position Visual FoxPro in relation to Microsoft Access?

    Microsoft Access, the database in Office, is the most broadly used and easiest-to-learn database tool that Microsoft offers. If you are new to databases, if you are building applications that take advantage of Microsoft Office, or if you want an interactive product with plenty of convenience, then choose Microsoft Access. Visual FoxPro is a powerful rapid application development (RAD) tool for creating relational database applications. If you are a database developer who builds applications for a living and you want ultimate speed and power, then choose Visual FoxPro.

    Q: Is Visual FoxPro part of Visual Studio .NET?

    No. Visual FoxPro 9.0 is a stand-alone database development tool which is compatible and evolutionary from previous versions of Visual FoxPro. Visual FoxPro 9.0 does not use or install the Windows .NET Framework. Visual FoxPro 9.0 is compatible with Visual Studio .NET the area of XML Web services, XML support, VFP OLE DB provider, and more. Visual FoxPro and Visual Studio are complimentary tools that work great together, such as Visual FoxPro 9.0 plus ASP.NET for adding WebForm front ends and mobile device front ends to Visual FoxPro applications.

    Q: What is Microsoft’s position on Visual FoxPro related to Visual Studio and .NET?

    We do not have plans to merge Visual FoxPro into Visual Studio and .NET, and there are no plans to create any sort of new Visual FoxPro .NET language. Instead, we are working on adding many of the great features found in Visual FoxPro into upcoming versions of Visual Studio, just like we’ve added great Visual Studio features into Visual FoxPro. If you want to do .NET programming, you should choose a .NET language with Visual Studio.

    A Message to the Community

    • Article
    • 08/29/2016

    March 2007

    We have been asked about our plans for a new version of VFP. We are announcing today that there will be no VFP 10. VFP9 will continue to be supported according to our existing policy with support through 2015 (https://support.microsoft.com/lifecycle/?p1=7992). We will be releasing SP2 for Visual FoxPro 9 this summer as planned, providing fixes and additional support for Windows Vista.

    Additionally, as you know, we’ve been working on a project codenamed Sedna for the past year or so. Sedna is built using the extensibility model of VFP9 and provides a number of new features including enhanced connectivity to SQL Server, integration with parts of the .NET framework, support for search using Windows Desktop Search and Windows Vista as well as enhanced access to VFP data from Visual Studio.

    Concurrently, the community has been using CodePlex (https://www.codeplex.com) to enhance VFP using these same capabilities in the VFPx project. Some of these community driven enhancements include:

    • Support for GDI+
    • An enhanced class browser
    • Support for Windows Desktop Alerts
    • An object oriented menu system
    • Integration with MSBuild
    • A rule-based code analysis tool similar to fxCop in Visual Studio
    • An Outlook Control Bar control

    To reiterate, today we are announcing that we are not planning on releasing a VFP 10 and will be releasing the completed Sedna work on CodePlex at no charge. The components written as part of Sedna will be placed in the community for further enhancement as part of our shared source initiative. You can expect to see the Sedna code on CodePlex sometime before the end of summer 2007.

    Visual FoxPro 6

    Technical Articles

    • Article
    • 06/30/2006

    ADO Jumpstart for Microsoft Visual FoxPro Developers

    Customizing Visual FoxPro 6.0 Application Framework Components

    Microsoft Transaction Serer for Visual FoxPro Developers

    The Microsoft Visual FoxPro 6.0 Component Gallery

    Microsoft Visual FoxPro 6.0 and Visual Studio Installer Tutorial

    Using Microsoft Visual Studio Installer for Distributing Visual FoxPro 6.0 Applications

    Using MSMQ with Microsoft Visual FoxPro 6.0

    The Visual FoxPro 6.0 Class Browser

    ADO Jumpstart for Microsoft Visual FoxPro Developers 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. What Are OLE DB and ADO?
    3. Why Incorporate ADO into a Visual FoxPro Application?
    4. ADO Object Model

    Show 2 more

    John V. Petersen

    April 1999

    Summary: Provides Microsoft Visual FoxPro developers with an overview of ActiveX Data Objects (ADO) and shows how to incorporate ADO into Visual FoxPro applications. Discusses the ADO object model and implementing Remote Data Services (RDS). (52 printed pages)

    Contents

    Introduction What are OLE DB and ADO? Why Incorporate ADO into a Visual FoxPro Application? ADO Object Model Remote Data Services Summary

    Introduction

    Microsoft®ActiveX® Data Objects (ADO) is perhaps the most exciting new Microsoft technology in quite some time. Because ADO is concerned with data, this new technology is of particular interest to Microsoft® Visual FoxPro® developers. Of course, you may ask, “Why do I need ADO? Visual FoxPro already has a high-performance local data engine.” It’s a good question.

    This paper provides the Visual FoxPro developer with a background of what ADO is and how to incorporate ADO into Visual FoxPro applications. After reading this paper, you should have enough information to readily answer the question: “Why do I need ADO?”

    A Brief Word About ADO Events

    One limitation of Visual FoxPro has been an inability to surface COM events. While Visual FoxPro can respond to events raised by ActiveX controls, objects created with the CreateObject function cannot. In Microsoft®Visual Basic®, COM Events are handled by using the WithEvents keyword. In Visual FoxPro, the new VFPCOM.DLL achieves the same results. The topics VFPCOM, ADO Events, and how to integrate ADO and Visual FoxPro will be discussed in another white paper. This paper is dedicated to providing the Visual FoxPro developer, with a comprehensive overview of ActiveX Data Objects, Remote Data Services (RDS), their respective objects, and how those objects work.

    This paper covers the following topics:

    • What are ADO and OLE DB?
    • Why incorporate ADO into a Visual FoxPro application?
    • The ADO object model
    • Remote Data Services

    What Are OLE DB and ADO?

    When discussing ADO, we are really talking about two distinct elements: the ActiveX data objects themselves and Microsoft Universal Data Access technology, more commonly known as OLE DB.

    OLE DB and Universal Data Access

    In simple terms, OLE DB is the succeeding technology to the Open Database Connectivity (ODBC) standard. OLE DB is a set of low-level interfaces that facilitate the Microsoft Universal Data Access strategy. ADO is a set of high-level interfaces for working with data.

    While both ODBC and OLE DB have the ability to make data available to a client, the capabilities of the two technologies are very different. ODBC is primarily designed for use on relational data. However, data exists in nonrelational as well as relational formats. In addition to new data formats, data resides in new places such as the Internet. Finally, the Microsoft Component Object Model (COM) framework requires better data access technology. Clearly, ODBC does not address these needs; a new technology is needed. That technology is OLE DB, and it is here to stay.

    The following graphic best illustrates how OLE DB and ADO work together. Clients can work directly with OLE DB or can work with OLE DB through the ADO interface (the latter is typically the case). Note that OLE DB can access SQL data either directly or through ODBC. An OLE DB provider provides direct access by OLE DB. Also note that OLE DB can also be used to access a variety of non-SQL data, as well as data that exists in mainframes. The ability to access data through a common interface, without regard to data location or structure, is the real power behind ADO and OLE DB.

    Whereas ODBC uses drivers, OLE DB uses providers. A provider is a software engine that provides a specific type of data that matches the OLE DB specification. Several OLE DB providers exist today, including those for Microsoft SQL Server™ and Oracle. Because there is such widespread use of ODBC, an OLE DB provider for ODBC has also been created in order to ease the migration from ODBC to OLE DB. Several nonrelational providers are currently under development. Perhaps the most anticipated of these is the OLE DB Provider for Microsoft Outlook®. A special provider, MS Remote, allows direct data access over the Internet. This brief list of providers shows the third-party community commitment to OLE DB, and many new providers are currently under development. For the latest news on available providers, refer to https://www.microsoft.com/data/.

    ADO Overview

    OLE DB is then a set of low-level interfaces that provide access to data in a variety of formats and locations. While powerful, OLE DB interfaces can be cumbersome to work with directly. Fortunately, ADO provides a set of high-level, developer-friendly interfaces that make working with OLE DB and universal data access a relatively simple task. Regardless of the programming environment you use, any Visual Studio® or Microsoft Office product such as Visual FoxPro, Visual Basic, Visual C++®, or Word, the interface you will use to access data remains constant. That interface is ADO, which in turn uses OLE DB.

    ADO itself is just a set of objects. By itself, ADO is not capable of anything. In order to provide any functionality, ADO needs the services of an OLE DB provider. The provider in turn uses the low-level OLE DB interface to access and work with data. One ADO connection may use a SQL Server OLE DB provider and another ADO connection may use an Oracle OLE DB provider. While the interface is constant, the capabilities may be very different because OLE DB providers are very different, which highlights the polymorphic nature of OLE DB.

    As developers, we crave consistency. ADO provides us with a consistent interface for our program code.

    ADO Version Summary

    The current version of ADO (2.1) is the fourth version of ADO to be released in less than two years. ADO 1.0 was primarily limited to working with Active Server pages. Only one OLE DB provider existed, the OLE DB Provider for ODBC Drivers.

    ADO (2.1)—Ships with the newest version of Microsoft Web browser, Internet Explorer 5.0. When discussing data or anything related to the Internet, it is almost impossible to do so without mentioning XML. XML, the Extensible Markup Language, is a mark-up language that allows users to create custom tags to describe data. XML is quickly becoming the universal format for storing and streaming data. The primary storage format in Office 2000 for document data will be XML. ADO (2.1) client-side recordsets can be saved as XML documents.

    ADO (2.0)—Represented a huge gain in functionality. One of the most notable new features was the ability to create client-side recordsets. To go along with this, also added were the abilities to create filters and indexes, and the ability to sort recordsets. These abilities are very much the same as those that exist with Visual FoxPro cursors. Finally, the ability to persist client-side recordsets was also added. In effect, data could be acquired from a server into a client-side recordset. The client-side recordset could then be saved as a file on the local hard-drive that could be opened at a later time without being connected to the network.

    ADO (1.5)—Introduced new capabilities and providers to ADO. Among the new providers was the OLE DB Provider for Jet (the JOLT Provider). The MS Remote Provider, which powers the Remote Data Services (RDS), was introduced as well. This version also introduced the ability to create disconnected recordsets.

    What You Need to Get Started

    In order to work through the examples presented in this paper, you will need the following:

    • Microsoft Visual FoxPro 6.0
    • Microsoft Data Access Components, which can be downloaded from https://www.microsoft.com/data/
    • SQL Server 6.5 or 7.0 with the sample Northwind database installed
    • A system DSN called TasTrade that points to the TasTrade Visual FoxPro Sample Database
    • A system DSN called Northwind that points to the SQL Server Northwind database

    Why Incorporate ADO into a Visual FoxPro Application?

    Have you ever wanted to pass a cursor as an argument to a function or class method? Or have you wanted to pass data to automation server applications such as Microsoft Word or Excel? Perhaps you have created a Visual FoxPro DLL and have needed a way to pass data from the user interface to a class method in the DLL. Maybe you have been looking for a way to stream data across the Web. If your answer is “yes” to at least one of these, ADO can help you today!

    Until now, the world of component-based development has lacked one thing: a method of effectively moving data between processes. Now, whether ADO is hosted by Visual FoxPro, Visual Basic, Excel, or Word, the interface is consistent. The new COM capabilities of Visual FoxPro 6.0 enable creating of ADO recordsets, populating them with data, and passing them to a variety of processes. This all goes to support the strategic positioning of Visual FoxPro, a creator of middle-tier components.

    Just about everything in Visual FoxPro is an object, except for reports, menus, and data. One of the biggest feature requests from Visual FoxPro developers has been the ability to work with data as a set of objects. Data objects provide several benefits, including an enhanced event model and the ability to overcome limitations of Visual FoxPro cursors. While many limitations are gone, many benefits of Visual FoxPro cursors have been retained. As you work with ADO, there’s good reason to think are many similarities to Visual FoxPro; ADO is based on the Visual FoxPro cursor engine. So, for those who have wanted data objects in Visual FoxPro, the wait is over with ADO.

    ADO is not a replacement for Visual FoxPro cursors. Rather, Visual FoxPro cursors and ADO are complementary. When used together, very powerful applications can result. The following pages detail the ADO object model and the common properties and methods you will work with, including:

    • Remote Data Services (RDS), technology which allows for the streaming of data over the Internet via HTTP.
    • VFPCOM.DLL, which enables the handling of COM events in Visual FoxPro.
    • ADO Integration into Visual FoxPro.

    This section has several comprehensive examples on strategies you may employ when integrating ADO into your Visual FoxPro Applications.

    ADO Object Model

    Connection Object

    ProgID: ADODB.Connection

    The purpose of the Connection object is to provide access to a data store. To illustrate, the following code creates an ADO Connection object:

    oConnection = CreateObject("adodb.connection")
    

    Once an ADO Connection object has been created, you can access its data store. An active connection can be established by providing a few pieces of key information and invoking the Open( ) method of the Connection object. The following code opens a connection to the Visual FoxPro TasTrade database:

    oConnection.Open("TasTrade")
    

    Alternatively, the following code accesses the SQL Server Northwind database:

    oConnection.Open("Northwind","sa","")
    

    These two examples work with the OLE DB Provider for ODBC drivers. Different OLE DB providers can be used as well. The following example sets some common properties of the Connection object and uses the OLE DB Provider for SQL Server:

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
          ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    

    The syntax of the ConnectionString property appears complicated. Fortunately, you don’t have to code this by hand. When you install the Microsoft Data Access Components (MDAC), you can create a data link file.

    To create a data link file:

    1. Right-click your desktop and choose New\Microsoft Data Link from the pop-up menu.
    2. Specify a name for the file.
    3. Right-click and select Properties to modify the file properties.
    4. In the Properties dialog box, click the Provider tab, and choose a provider.
      The OLE DB Provider for ODBC is the default choice. For this example, select the OLE DB Provider for SQL Server.
    5. Click the Connection tab.
    6. Specify the name of the server, your user name and password, and the name of the database you wish to connect to.
    7. Open the UDL file in Notepad.Now, it is just a matter of copying and pasting the information. Alternatively, you can use the file itself:oConnection.Open(“File Name=c:\temp\test.udl”)

    ADO recognizes four arguments in the ConnectionString:

    • File Name: Specifies the name of a UDL file to use.
    • Provider: Specifies the name of an OLE DB provider to use.
    • Remote Provider: Specifies the name of a provider to use with Remote Data Services (RDS).
    • Remote Server: Specifies the server on which data resides when using Remote Data Services (RDS).

    Any additional arguments passed in the ConnectionString are passed through to the OLE DB provider being used.

    In addition to the Open method, the following are the common methods you are likely to use with the Connection object:

    • BeginTransCommiTrans, and RollBackTrans—These methods work like the Begin Transaction, End Transaction, and RollBack statements in Visual FoxPro. The Connection object controls all transaction processing. For more detail, see the section Transactions/Updating Data. Note that not all OLE DB providers support transaction processing.
    • Close—This method closes an open Connection object.
    • Execute—This method runs a SQL statement, stored procedure, or OLE DB provider-specific command. In reality, a Command object, which actually does the work of executing the command, is created on the fly. More on the Command object and the flat object hierarchy of ADO later in this paper.
    • OpenSchema—This method returns information regarding defined tables, fields, catalogs, and views into an ADO Recordset object. This method works like the DBGetProp( ) function in Visual FoxPro.

    Errors collection

    ADO does not trap errors, nor does it have an error handler. Instead, ADO can record the occasions when errors occur. It is up to the host application, Visual FoxPro in this case, to both trap and handle the error. ADO only reports what errors have occurred. Note that the error is actually reported by the specific OLE DB provider. ADO is merely a vehicle to report the error.

    The Errors collection is part of the Connection object and consists of zero or more Error objects. When an error occurs, an Error object is appended to the Errors collection. The following code illustrates how the Errors collection works. In this example, the name of the database has been misspelled purposely in order to generate an error:

    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Nothwind;Data Source=JVP"
       .Open
    EndWith
    */ At this point an error will occur – causing VFP's default error
    */ handler – or the active error handler to invoke
    */ At this point, we can query the Errors Collection of the
    */ Connection Object
    For Each Error In oConnection.Errors
       ?Error.Description,Error.Number
    Next Error
    

    Recordset Object

    ProgID: ADODB.Recordset

    Once you establish an ADO connection, you can open a recordset of data. The Recordset object is very much like a Visual FoxPro cursor. Like the Visual FoxPro cursor, an ADO recordset consists of rows of data. The recordset is the primary object that you will use while working with ADO. Like the Connection object, the Recordset object also provides an Open method. To illustrate, the following code opens the Customer table of the Visual FoxPro Tastrade database:

    oRecordSet = CreateObject("adodb.recordset")
    oRecordSet.Open("Select * From Customer",oConnection)
    

    The first argument of the Open method specifies the source of data. As you will see, the source can take on several forms. The second argument of the Open method specifies a connection to use for retrieving the data specified by the source. At a minimum, this is all you need to open a recordset. Additional examples will expand on the additional arguments the Open method accepts.

    With a Recordset object created, one of the most common actions you will perform is navigating through records. Depending on the type of ADO recordset that has been created, certain navigational capabilities may or may not be available. The different types of possible ADO recordsets will be discussed shortly. The following code illustrates how to navigate through an ADO recordset:

    Do While !oRecordSet.Eof
       oRecordset.MoveNext
    EndDo
    

    The following paragraphs briefly describe the most common recordset properties and pethods you are likely to use. It is by no means a replacement for the ADO documentation, which gives both a complete description of the properties and methods and complete descriptions of acceptable enumerated types and arguments. ADO is well documented in the Microsoft Data Access Components (MDAC) SDK. You can download the MDAC SDK from https://www.microsoft.com/data.

    In addition, I highly recommend ADO 2.0 Programmers Reference, by David Sussman and Alex Homer, from Wrox Press.

    RecordSet types

    You can create four types of recordsets in ADO:

    • Forward Only—This type of recordset can be navigated only in a forward direction. It is ideal when only one pass through a recordset is required. Examples include populating a List box or a Combo box. The RecordCount property is irrelevant with this type of recordset.
    • Keyset—This type of recordset keeps acquired data up to date. For example, if you retrieve 100 records, data modified by other users to those 100 records will be visible in your recordset. However, modifications regarding new or deleted records made by other users will not be visible in your recordset. Both forward and backward navigation are supported. The RecordCount property returns a valid value with this type of recordset.
    • Dynamic—With this type of recordset, all underlying data is visible to the Recordset object. Because the number of records in the underlying table can change, the RecordCount property is irrelevant with this type of cursor. However, forward and backward navigation are supported.
    • Static—Both the number of records and data are fixed at the time the Recordset object is created. The only way to get the latest version of data and all records is to explicitly invoke the Requery method. You can use the RecordCount property. In addition, both forward and backward navigation is permitted.

    RecordSet locations

    Recordset objects can exist in either of two locations, the server or the client:

    • Server—The most common examples of server-side ADO recordsets are those created through Active Server Pages (ASP).
    • Client—A recordset that resides on a workstation is useful when creating disconnected recordsets or recordsets on which you wish to apply filters, sorts, or indexes.

    The most common properties you are likely to use with ADO recordsets include the following:

    • ActiveCommand property—An object reference to the Command object that created the recordset.
    • ActiveConnection property—An object reference, to the Connection object, that provides the link to an underlying data source.
    • AbsolutePosition property—Specifies the relative position of a record in an ADO recordset. Unlike the Bookmark property, which does not change, the AbsolutePosition property can change depending on the active sort and filter.
    • Bookmark property—A unique record identifier that, like the record number in a Visual FoxPro cursor or a record number in Visual FoxPro, does not change during the life of a recordset.
    • BOF/EOF properties—Beginning of File and End of File, respectively, that work just like the BOF( ) and EOF( ) functions in Visual FoxPro.
    • EditMode property—Specifies the editing state of the current record in an ADO recordset.
    • Filter property—The string that represents the current filter expression. This property is like the SET FILTER statement in Visual FoxPro. Unlike the Find method, multiple expressions linked with AND or OR operators are allowed. This property is only applicable to client-side recordsets.
    • Sort property—A comma-delimited set of fields that specifies how the rows in an ADO recordset are sorted. This property is only applicable to client-side recordsets.
    • State property—Specifies the state of an ADO recordset. Valid State properties are closed, open, connecting, executing, or fetching.
    • Status property—Specifies the editing status of the current record. Valid Status properties include unmodified, modified, new, and deleted. This property can be any one of the values contained in RecordStatusEnum.
    • MarshalOptions property—Specifies how records are returned (marshaled) to the server. Either all or only modified records can be returned. This property is only applicable to client-side disconnected recordsets
    • MaxRecords property—Specifies the total number of records to fetch from a data source.
    • RecordCount property—Specifies the number of records in a recordset. This property is like the Recc( ) function in Visual FoxPro.
    • Source property—Specifies the command or SQL statement that provides data for the recordset.

    Note   The type and location of a cursor as well as the OLE DB provider you select will affect the recordset properties that are available.

    Use the following table as a guide to help you make the right recordset type and location decision:

    Table 1. PropertiesExpand table

    TypeBookmarkRecordCountSortFilterMarshalOptions
    Forward Only     
    Key Set44   
    Dynamic     
    Static: Client44444
    Static: Server44   

    Only client-side recordsets can be sorted and filtered. If the CursorLocation property of ForwardOnlyKeySet, and Dynamic recordset types is set to adUseClient, making them client-side cursors, the CursorType property is automatically coerced to the Static Cursor type.

    Note   This is the behavior of the OLE DB Provider for SQL Server. The OLE DB Provider for ODBC supports only ForwardOnly and Static recordsets, regardless of where the recordset resides.

    As with properties, method availability can also vary:

    Table 2. Available MethodsExpand table

    TypeMoveFirstMovePreviousMoveNextMoveLastResyncRequery
    Forward Only  4  4
    Key Set4444 4
    Dynamic4444 4
    Static – Client444444
    Static – Server4444 4

    The following list describes some of the common methods you will use in the ADO Recordset object:

    • MoveFirstMovePreviousMoveNextMoveLast, and Move methods—Navigational methods that work as their respective names imply. The Move method accepts two arguments, the number of records to move and the position from which to begin the move. The Move method is similar to the Go statement in Visual FoxPro. MoveFirst and MoveLast work like Go Top and Go Bottom, respectively. Finally, MovePrevious and MoveNext work like Skip 1 and Skip –1, respectively.
    • Find method—Accepts a criterion string as an argument and searches the recordset for a match. If a match is not found, depending on the search direction, either the BOF or EOF property will evaluate to true (.T.). This method works much the same way as the Seek and Locate statements in Visual FoxPro. Unlike the Filter property and the Seek and Locate statements in Visual FoxPro, the ADO Recordset object does not allow multiple search values joined by the And or the Or operator. Using anything other than a single search value will result in an error.
    • Open method—Opens an existing ADO Recordset object. This method accepts several arguments and is discussed in detail later in this section.
    • Close method—Closes an ADO Recordset object. Many properties, such as CursorType and LockType, although read/write, cannot be modified while the recordset is open. The Close method must be invoked before those and other properties are modified.
    • Update and UpdateBatch methods—Update writes changes for the current record to the underlying data source; UpdateBatch writes pending changes for all modified records to the underlying data source. The UpdateBatch method is only relevant when Optimistic Batch Locking is used.
    • Cancel and CancelBatch methods—The Cancel method cancels modifications made to the current record; the CancelBatch method cancels pending changes to all modified records.
    • Resync method—Refreshes the Recordset object with data from the underlying data source. Invoking this method does not rerun the underlying command. Options exist for which records are actually refreshed.
    • Requery method—Unlike the Resync method, reruns the underlying command, which causes any pending changes to be lost. In effect, issuing a Requery is like invoking the Close method then immediately invoking the Open method.
    • Supports method—Specifies whether or not the recordset supports a function, based on a passed argument. For example, you can use this method to specify whether a recordset supports bookmarks, or the addition or deletion of records, or the FindUpdate, and UpdateBatch methods, to name a few. Because what is supported is depends on the OLE DB provider used, it is a good idea to use this method to make sure a needed function is supported.
    • GetRows method—Returns a set of records into an array.
    • GetString method—Returns a set of records into a string.

    The moral of the story is that before relying on the existence of anything in ADO, know and understand the OLE DB provider you are using, because the capabilities available to you can vary dramatically.

    Lock types

    There are four different locking schemes in ADO recordsets. These locking schemes are similar to those in Visual FoxPro.

    • Read-Only—As the name indicates, the recordset is opened for read-only purposes only. When you don’t need to modify data, this is the best locking scheme to use from a performance standpoint. This scheme applies to both server and client-side recordsets.
    • Lock Pessimistic—In this scheme, a lock attempt is attempted as soon as an edit is performed. This locking scheme is not relevant for client-side recordsets. Pessimistic Locking in an ADO recordset is like Pessimistic Locking with Row Buffering in a Visual FoxPro cursor.
    • Lock Optimistic—In this scheme, a lock attempt is made when the Update method is invoked. This locking scheme applies to both server and client-side recordsets. Optimistic Locking in an ADO recordset is like Optimistic Locking with Row Buffering in a Visual FoxPro cursor.
    • Lock Batch Optimistic—This scheme is like the Lock Optimistic scheme, except that more than one row of data is involved. In this scheme, a lock is attempted on modified records when the UpdateBatch method is invoked. This scheme is like Optimistic Locking with Table Buffering in a Visual FoxPro cursor.

    The following table illustrates the availability of some common methods depending on the locking scheme used:

    Table 3. Method Availability (Depending on Lock Type)Expand table

    Lock TypeCancelCancelBatchUpdateUpdateBatch
    Read Only4   
    Pessimistic4444
    Optimistic4444
    Optimistic Batch4444

    With the concepts of cursor types, locations, and locking schemes out of the way, we can discuss the real abilities of ADO recordsets. The most notable of these abilities are updating, sorting, and filtering of data. Before undertaking that discussion, however, take a few moments to review the Fields Collection object.

    Fields collection object

    Associated with the Recordset object, is the Fields Collection object. The Fields Collection object contains zero or more Field objects. The following code enumerates through the Fields Collection of a Recordset object:

    For Each ofield In oRecordset.Fields
       With oField
          ?.Name,.Value,.Type,.DefinedSize
          ?.ActualSize,.NumericScale,.Precision
       EndWith
    Next oField
    

    The common Field properties you will work with:

    • Name—Specifies the name of the Field object. This corresponds to the name of the data element in the underlying data source. It is easy to define the name element as the name of the field in the underlying table. However, note that ADO and OLE DB work with both relational and nonrelational data. Given that, while you may be working with ADO, the underlying data may come from Outlook, Excel, Word, or Microsoft® Windows NT® Directory Services.
    • Value—Indicates the current value of the Field object.
    • OriginalValue—Indicates the Value property of the Field object before any modifications where made. The OriginalValue property returns the same value that would be returned by the OldVal( ) function in Visual FoxPro. When you invoke the Cancel or CancelUpdate methods of the Recordset object, the Value property of the Field object is replaced by the contents of the OriginalValue property. This behavior is similar to that exhibited when TableRevert( ) is issued against a Visual FoxPro cursor.
    • UnderlyingValue—Indicates the current value in the data source. This property corresponds most closely to the CurVal( ) function in Visual FoxPro. To populate the Value property of each Field object in the Fields collection, you need to invoke the Resync method of the Recordset object. With a client-side cursor, this property will return the same value as the OriginalValue property, since the recordset may or may not have an active connection.
    • Type—Indicates the data type of the Field object. The value of this property corresponds to a value contained in DataTypeEnum. Examples of values in DataTypeEnum are adBoolean, adInteger, and adVarChar.
    • Defined Size—Specifies the size of the field containing a data element in the data source. For example, in SQL Server, the Country field in the Customers table of the Northwind database is 15 characters long. Therefore, the DefinedSize property of the Country Field object is 15.
    • ActualSize—Represents the length of the actual data element in a datasource. To illustrate, consider the Country Field object again. In the case where the value is Germany, the ActualSize property is 7, while the DefinedSize property is still 15.
    • NumericScale—Specifies how many digits to the right of the decimal place are stored.
    • Precision—Specifies the maximum number of digits to be used for numeric values.

    In addition to these properties, GetChunk is one interesting method you are likely to use. This method allows you to progressively fetch portions of the contents of a field object. This method is very useful when dealing with large text fields. It can be used only on fields where the adFldLong Bit set of the Attributes property is set to true (.T.). See the next section for details on the Attributes property. Understand that fields of the type ADLongVarChar have the adFldLong Bit set. The Notes field of the Employees table is of the type adLongVarChar.

    The following code fetches data from the notes field in 10-byte chunks:

    Local nBytesRead,cChunkRead 
    nBytesRead = 0
    cChunkRead = Space(0)
    Do While .T.
       nBytesRead = nBytesRead + 10
       cChunkRead = oRecordset.Fields("notes").GetChunk(10)
       If IsNull(cChunkRead) Or;
        nBytesRead > oRecordset.Fields("notes").ActualSize
          Exit
       Else
          ?cChunkRead
       Endif   
    EndDo
    

    Successive calls to GetChunk continue where the previous call ended. The GetChunk method is very useful when you need to stream data or only need to see the first few characters of a large text field.

    Along with GetChunk, examine the AppendChunk method. The first time this method is called for a field, it overwrites any data in the field. Successive calls then append the data, until pending edits are cancelled or updated. The following code illustrates how this method works:

    For x = 1 To 100
       oRecordset.Fields("notes").AppendChunk(Str(x)+Chr(10)+Chr(13))
    Next x
    

    Both the GetChunk and AppendChunk methods are ideal for dealing with low memory scenarios.

    The Attributes property

    An attribute specifies the characteristics of something. As a person, you have many attributes, eye color, height, weight, and so forth. In the OOP world, objects have many attributes. Most of the time, attributes are exposed in the form of properties. A Visual FoxPro form has several properties such as WidthHeight, and BackColor, just to name a few. The same is true for objects in ADO. Sometimes, however, it is not convenient to have a one-to-one correspondence between attributes and properties. Often, you can pack large amounts of information into a smaller space through the power of setting bits. A bit is much like a switch. It is either on or off or 1 or 0. If you string these bits together, you gain the ability to store multiple values in a small space. This is how the Attributes property works.

    The ConnectionParameterField, and Property objects all have an Attributes property. If you have never worked with bit operations before, working with this property can be quite challenging. In some situations, as is the case with the GetChunk and AppendChunk methods, you will need to refer to the Attributes property of the Field object to determine whether those methods are available.

    Using the Field object to illustrate how the Attributes property works, you can associate the following attributes with a Field object and its associated binary values:

    • AdFldMayDefer—Indicates that the field contents are retrieved only when referenced—0x00000002
    • adFldupdateable—Indicates that the field can be updated—0x00000004
    • adFldUnkownupdateable—Indicates that the provider does not know whether the field is updateable—0x00000008
    • adFldFixed—Indicates that the field contains fixed length data—0x00000010
    • adFldIsNullable—Indicates that the field can accept a null value during a write operation—0x00000020
    • adFldMayBeNullable—Indiates that the field may contain a null value—0x00000040
    • adFldlong—Indicates that the field contains long binary data and that the GetChunk and AppendChunk methods can be used—0x00000080
    • adFldRowID—Indicates that the field contains a row ID and cannot be updated. This does not relate to a field that may contain the identity value or some other auto-incrementing value. Rather, it relates to a ROW ID that is unique across the database. Oracle has this feature—0x00000100
    • adFldRowVersion—Indicates whether the field indicates the version of the row. For example, a SQL TimeStamp field may have this attribute set—0x00000200
    • adFldCachedDeferred—Indicates that once this field has been read, future references will be read from the cache—0x00001000

    Usually, more than one of these attributes are present at any given time. Yet the Attributes property is a single value. Using the Employees table Notes field as an example, you will see that the Attributes property yields a value of 234. The value 234 represents the sum of the attributes for that field. For example, nullable and long attributes have decimal values of 32 and 128 respectively. This means that the Attributes property evaluates to 160. This works like the Windows Messagebox dialog box with regard to specifying the icon and types of buttons that are present.

    Knowing that the Attributes property is a sum of the attributes of a Field object does not help in determining whether a specific attribute is present. This is where understanding bit operations comes in handy. The first step is to convert the sum (such as 234, above) into a binary equivalent:

    11101010
    

    Working from right to left, (or from the least significant bit to the most significant)—and beginning with zero, see that bits 1, 3, 5, 6, and 7 are set, (indicated by their values of 1 in those positions). Bits 0, 2, and 4 are not set. The next step is to determine whether a field is “long.”

    To determine whether a field is a long field, we must first convert the adFldLong constant, which specifies which bit if set, indicates that the field is long. The adFldLong constant has a hex value of 0x00000080. This translates into a decimal value of 128. The following is the binary equivalent:

    10000000
    

    Converting a hex value to decimal in Visual FoxPro is simple. The following code illustrate how to convert hexadecimal values to decimal:

    x = 0x00000080
    ?x && 128
    

    And, if you ever need to convert back to hexadecimal:

    ?Transform(128,"@0") && 0x00000080 
    

    Using our original hex value, 11101010, and working from right to left and beginning with zero, see that the seventh bit is set. Therefore, the seventh bit of the Attributes property, if set, means the field is long. Going further, whatever attributes occupy bits 1, 3, 5, and 6, also apply to this field. The following table of field attributes should help to sort things out:

    Table 4. Field AttributesExpand table

    Hex ValueDecimal ValueField Attribute ConstantBit
    0x000000022AdFldMayDefer1
    0x000000044AdFldupdateable2
    0x000000088AdFldUnkownUpdateable3
    0x0000001016AdFldFixed4
    0x0000002032AdFldIsNullable5
    0x0000004064AdFldMayBeNull6
    0x00000080128AdFldLong7
    0x00000100256AdFldRowID8
    0x00000200512AdFldRowVersion9
    0x000010004096AdFldCacheDeferred12

    So, along with being a long field, the field is deferred, updateable, can have a null written to it, and it may also already contain a null value. Visually, this makes sense. How can you do this programmatically?

    If you refer to online examples (almost always programmed in Visual Basic), you will see code like this:

    If (oField.Attribute AND adFldLong) = adFldLong 
       ' The field is long
    End If
    

    This is pretty slick in that you can test for whether a specific attribute bit is set by using the AND operator with the attribute property and the constant. If you try this in Visual FoxPro, you will get data type mismatch errors. Fortunately, there is a way. Visual FoxPro contains a host of bit functions. One function, BITTEST, does as its name implies. It tests whether a specified bit in a passed argument is set. To review, we need to see if the seventh bit in the value 234 is set. The following Visual FoxPro code demonstrates how to use the BITTEST function:

    If BitTest(234,7)
       */ The Field is long
    Endif
    

    To find out if the field is nullable:

    If BitTest(234,5)
       */ The Field is long
    Endif
    

    The Attributes property of the ConnectionParameter, and Property objects works in the same manner as illustrated above. The differences are the names and quantity of attributes that are present.

    ADO and COM defined constants

    ADO and OLE DB, like any COM components, make extensive use of defined constants in the examples that document the usage of properties, events, and methods. Other development environments in Visual Studio such as Visual Basic and Visual Interdev provide IntelliSense technology, because of their respective abilities to interact directly with the type libraries of COM components. For these development environments, you can reference defined constants just as if they were a part of the native language. So, working with published examples is a fairly trivial task. On the other hand, in the Visual FoxPro development environment there is, in fact, a bit of a challenge. The question always seems to be “How can I use the Visual Basic samples in Visual FoxPro?” The biggest stumbling block is usually in finding the value of the defined constants. In Visual FoxPro, you need to use the #Define statement for each constant.

    One solution for obtaining the value of the ADO defined constants is to obtain the MDAC SDK from Microsoft. The MDAC SDK can be downloaded from https://www.microsoft.com/data/download.htm.

    Once you install the SDK, locate the Include\ADO directory. In that directory, you will find the ADOINT.H file, which contains all of the enumerated types and the values for the defined constants.

    A second, and perhaps easier, solution is to use the resources already installed on your machine. If you are working through the sample code in this paper, you already have the Microsoft Data Access Components installed on your workstation. The Visual Basic Development Environment (both the full Visual Basic IDE and the Visual Basic Editor in desktop applications like Word and Excel) has a great resource called the Object Browser. This could, in fact, be the most underutilized tool on the planet.

    To illustrate its functionality, open any desktop application that uses Visual Basic, such as Word or Excel. Or, if you have the Visual Basic Programming System installed, you can open that as well.

    If you opened a VBA application

    1. From the View menu, choose Toolbars.
    2. From the Toolbars menu, choose Visual Basic.
    3. On the Visual Basic toolbar, click Visual Basic Editor.
    4. From the Tools menu, choose References.
    5. Check the Microsoft Data Access Objects 2.x Library.

    If you opened the Visual Basic IDE

    1. Create an empty project.
    2. From the Project menu, select References.
    3. Check the Microsoft Data Access Objects 2.x Library.

    Now, whether you are in the VBA Editor or the VB IDE

    1. Press F2 to display the Object Browser.
    2. In the first combo box, select ADODB.
    3. In the second box, type ADVARCHAR.
    4. Press Search
      or
      Press Enter.

    Clearly, the Object Browser is a powerful tool for the developer who works with COM components. Not only are the defined properties, events, and methods accessible in the Object Browser, so also are the defined constants and their respective values. Notice the value of adVarChar in the lower pane of the Object Browser.

    Opening, sorting, and filtering data

    One of the big advantages of using a development platform such as Visual FoxPro is its local data engine. Not only does the engine provide superior query performance, but it also provides some very flexible capabilities when it comes to both working with and presenting data. There isn’t a Visual FoxPro application that fails to sort or filter data to some degree. In Visual FoxPro, sorting is accomplished by creating a set of index tags for a table. Filtering is accomplished by using the Set Filter command. Fortunately, ADO has these capabilities as well.

    You can see in the Field Attribute table that the availability of features depends on the location in which the recordset is created. It is clear that we must ensure that a client-side recordset is created.

    For example, create a Connection object to the TasTrade or SQL Server Northwind database. The following code assumes that the Connection object, oConnection, has been created before you open the Recordset object.

    First, we need to implement a few required #Defines:

    #Define adUseClient   3
    #Define adLockBatchOptimistic   4
    #Define adCmdTable   2
    

    For SQL Server:

    With oRecordset
       .Source = "Customers"
       .ActiveConnection = oConnection
       .CursorLocation = adUseClient
       .LockType = adLockBatchOptimistic
       .Open
    EndWith
    

    Or

    oRecordset.Open("Customers",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic)
    

    For Visual FoxPro:

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Customer"
       .CursorType = adOpenStatic
       .LockType = adLockReadOnly
       .CursorLocation = adUseClient
       .Open(,,,,adCmdtable)
    EndWith
    

    Or

    oRecordset.Open("Customer",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic,;
                     adCmdTable)
    

    Or

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Select * From Customer"
       .CursorType = adOpenStatic
       .LockType = adLockReadOnly
       .CursorLocation = adUseClient
       .Open
    EndWith
    

    Or

    oRecordset.Open("Select * From Customer",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic)
    

    SQL Server and Visual FoxPro open data differently. Remember that when using SQL Server, you are using the OLE DB Provider for SQL Server. When you access data in Visual FoxPro, use the OLE DB Provider for ODBC, since there is no native OLE DB provider for Visual FoxPro.

    The difference rests with the optional fifth argument of the Open method. The SQL Server OLE DB Provider is designed to recognize when you pass just a table name. With the ODBC OLE DB Provider, you must specify how it should interpret the Source property. By default, the ODBC OLE DB Provider expects a SQL statement. When you pass a SQL statement, there is no need to explicitly state how the provider should interpret things. The Visual FoxPro ODBC driver generates an “Unrecognized Command Verb” error message if you only specify a table name as the source and you fail to use the optional fifth argument. Note that if you use the ODBC OLE DB Provider to access SQL Server, you must employ the same technique that is needed for Visual FoxPro.

    Which method should you employ when you populate the properties individually before invoking the Open method or passing the arguments to the Open method? Once again, it is a matter of preference. Of the two, manually populating the properties makes for more readable code.

    Sorting and filtering data are just matters of manipulating the Sort and Filter properties respectively. The following code sorts the recordset created from TasTrade in the example above, by country, ascending, then by region, descending:

    oRecordset.Sort = "Country,Region Desc"
    

    The following code displays the sort and the functionality of the AbsolutePosition and Bookmark properties.

    oRecordset.MoveFirst
    Do While Not oRecordset.Eof
       With oRecordset
          ?.Fields("country").Value,;
           .Fields("region").Value,;
           .AbsolutePosition,;
           .Bookmark 
           .MoveNext
       EndWith
    EndDo
    

    Setting a filter is as easy as setting the sort. The following code filters for records where the country is Germany:

    oRecordset.Filter = "Country = 'Germany'"
    

    The Filter property also supports multiple values:

    oRecordset.Filter = "Country = 'Germany' Or Country = 'Mexico'"
    

    Finally, wild card characters are also supported:

    oRecordset.Filter = "Country Like 'U*'"
    

    To reset either the Filter or Sort properties, set them equal to an empty string:

    oRecordset.Sort = ""
    oRecordset.Filter = ""
    

    Finding data

    Another important capability of an ADO recordset is the ability to find records based on a search string. This capability works like searching for records in a Visual FoxPro cursor. Unlike the Seek or Locate statement in Visual FoxPro, the Find method provides control over the scope of records that are searched. The following code searches for a country that begins with the letter “B.”

    oRecordset.Find("country Like 'B%'")
    

    Although multiple criteria are not allowed, wild card searches are permitted:

    oRecordset.Find("country Like 'U*'")
    

    Searches for multiple criteria, such as the following, would result in an error:

    oRecordset.Find("country Like 'G*' Or country Like 'B*'")
    

    Transactions/updating data/conflict resolution

    Updating data in an ADO recordset is a fairly simple process. As in any environment, conflict resolution in multi-user environments is always an issue to be dealt with. This is where the Errors collection comes into play. Error trapping and handling needs to become an integral part of your ADO-related code. The following code samples employ a simple error handling scenario and use the Errors collection to determine whether conflicts have occurred. For a complete list and description of ADO error codes, consult the online documentation.

    When you update data, you can update either a single row, or several rows at a time in batch mode. These methods most closely correspond to row and table buffering, respectively, in Visual FoxPro. Building on the recordset already created, the lock type is Batch Optimistic. While updates are normally conducted in batches, you can also update one row at a time, just as in Visual FoxPro.

    The following code modifies the CompanyName field and attempts to update the SQL Server data source:

    oRecordset.Fields("companyname").Value = "Ace Tomato Company"
    oRecordset.Update
    

    Depending on a variety of scenarios, this code may or may not work. Perhaps a contention issue exists? Perhaps the user does not have rights to modify data. Hundreds of issues can cause an attempted update to fail. Therefore, anytime you attempt an update, you should employ error trapping. The following code expands the previous example and makes it a bit more robust:

    Local Err,cOldErr,oError
    cOldError = On("Error")
    On Error Err = .T.
    oRecordset.Fields("companyname").Value = "Ace Tomato Company"
    oRecordset.Update
    If Err
       For Each oError In oRecordset.ActiveConnection.Errors
          With oError
             ?.Number,.Description
          EndWith
       Next oError
    Endif
    On Error &cOldErr
    

    If you are thinking, “Hey, maybe I should write a wrapper class to better encapsulate and centralize code,” you’re on the right track. The following code creates a custom class that can serve as a starting point:

    Local oRecordsetHandler
    oRecordsetHandler = CreateObject("RecordsetHandler")
    oRecordset.Fields("companyname").Value = "Alfreds Futterkiste"
    If !oRecordsetHandler.Update(oRecordset)
       oRecordsetHandler.Cancel(oRecordset)
    Endif
    Define Class RecordsetHandler As Custom
       Protected oRecordset
       Protected ErrFlag
       
       Procedure Update(oRecordset)
          This.oRecordset = oRecordset
          oRecordset.UpdateBatch
          Return !This.ErrFlag
       EndProc
       Procedure Cancel(oRecordset)
          This.oRecordset = oRecordset
          oRecordset.Cancel
          Return !This.ErrFlag
       EndProc
       
       Procedure Error(nError, cMethod, nLine)
          Local oError
          For Each oError In This.oRecordset.ActiveConnection.Errors
             With oError
                ?.Number,.Description
             EndWith
          Next oError
          This.ErrFlag = .T.
       EndProc
    EndDefine
    

    There’s a better way to determine whether an update proceeded successfully. The preferred approach is to trap events that ADO fires. Visual FoxPro by itself does not surface COM Events. Fortunately, the new VFPCOM.DLL component provides this capability to Visual FoxPro. The previous example can be modified to show how using COM Events makes for more robust code and class design.

    Now we can improve the code of our example. Most of the time, for efficiency, you will want to batch your updates that comprise multiple records. Often, when you update multiple records, transaction processing is required. In other words, either updates to all records must succeed or none should occur. To illustrate, let’s say you must apply a 10 percent price increase to the products you sell. The prime requirement is that all records in the Products table need modification. Without transactional capabilities, the possibility exists that, for example, after the first 10 records are updated, an error generated on the eleventh record prevents a complete update. Transaction processing provides the ability to rollback changes.

    The following example incorporates error trapping and the three transaction methods of the Connection object:

    Local Err,cOldErr
    cOldErr = On("error")
    On Error Err = .T.
    oRecordset.ActiveConnection.BeginTrans
    Do While !oRecordset.Eof
       If Err
          Exit
       Else
          With oRecordset
             .Fields("unitprice").Value = ;
                .Fields("unitprice").Value * 1.1
             .Movenext
          EndWith   
       Endif
    EndDo
    oRecordSet.UpdateBatch
    If Err
       oRecordset.ActiveConnection.RollBackTrans
       oRecordset.CancelBatch
    Else
       oRecordset.ActiveConnection.CommitTrans
    Endif   
    On Error &cOldErr 
    

    Additional operations you are likely to employ with recordsets deal with adding new records and deleting existing records. Both of these processes are very simple. The following code adds a new record:

    oRecordset.AddNew
    

    As in Visual FoxPro, in ADO the new record becomes current. Once the AddNew method is invoked, the field can be populated and, depending on the LockType, you then invoke either the Update or UpdateBatch methods to modify the data source.

    Deleting records is just as easy. The following code deletes the current record:

    oRecordset.Delete
    

    Once again, after deleting the record, a call to Update or UpdateBatch will update the data source.

    SQL Server identity fields and parent/child relationships

    SQL Server, like most server RDBMSs and Microsoft® Access®, creates an auto-incrementing field that can serve as a primary key for a table. Typically, the data type for this field is Integer. In SQL Server, this type of field is called the Identity field. Fields of this type are read-only. It begs the question, “When adding records, how can one determine what these values are?” Knowing that the next generated value is a requirement for maintaining referential integrity when child tables are involved. The following example code shows a recordset in which the first field, ID, is the auto-incrementing field. After new field is added, checking the value of the ID field yields a character with a length of zero. Attempting to update the field results in an error. However, once the recordset is updated, checking the value again will yield a valid identity value.

    oRecordset.AddNew
    ?oRecordset.Fields("id").Value && empty string
    oRecordset.UpdateBatch
    ?oRecordset.Fields("id").Value && returns new identity value
    

    With the new identity value available, you can add records in child tables, using the identity value in the parent table as the foreign key in the child tables.

    But, what do you do in cases where you have disconnected recordsets?

    This section details an important capability in ADO—the ability to have recordsets without an active connection to the backend data source. At this point you can freely add new records to disconnected records. When the recordset is eventually reconnected, those newly added records are then sent to the backend data source. How do you know what the identity value will be in those cases? Simply put, you don’t know. At the same time, however, you still need to be able to add both parent and child records locally. You need some method that maintains the relationship locally, while at the same time, supporting the use of the identity value when the data is sent to the backend.

    The simplest solution to this problem is to include a field in each table that serves as the local ID. You need this extra field because the identity field will be read-only. On the client side, you can use several methods for producing an ID that is unique. One approach is to use the Windows API to fetch the next Global Unique Identifier (GUID). The following procedure outlines how the local process unfolds:

    1. Add a new parent record.
    2. Fetch the next GUID.
    3. Update the local primary key column with the GUID.
    4. Add a new child record.
    5. Update the local primary key column with the GUID.
    6. Update the foreign key column of the child with the GUID from its parent.

    At some point, you will reconnect to the server. The update process could be performed within the context of a transaction, done one row at a time by navigating through each record. Checking the recordset Status property, which indicates whether the current record has been newly created, modified, deleted, and so on, determines whether the current row should be sent back to the server. If the record should be sent back, the parent record can be updated via the UpdateBatch method. The UpdateBatch method accepts an optional argument that specifies that only the current record be updated. By default, UpdateBatch works on all records. If the value of one is passed—corresponding to the adAffectCurrent constant—only the current record is updated. Once the update occurs, the identity value generated by the server is available. This value would then be used to update the foreign key columns of any related children. Once that process is complete, the records for that parent would be sent back to the server as well. This same process would be used if grandchild and great-grandchild relationships also existed.

    The following Visual FoxPro code, from Visual FoxPro 6 Enterprise Development, by Rod Paddock, John V. Petersen, and Ron Talmage (Prima Publishing), illustrates how to generate a GUID:

    Local oGuid
    oGuid = CreateObject("guid")
    ?oGuid.GetNextGuid( )
    */ Class Definition
    Define Class guid AS Custom
      */ Create protected members to hold parts of GUID
      Protected data1
      Protected data2
      Protected data3
      Protected data4
      Procedure GetNextGuid
        */ The only public member. This method will return the next GUID
        Local cGuid
        cGuid = This.Export( )
        UuidCreate(@cGuid)         
        This.Import(cGuid)            
        cGuid = This.Convert(cGuid)
        Return cGuid
      EndProc
      Protected Procedure bintoHex(cBin)
        */ This method converts a binary value to Char by calling the Hextochar
    
        */ Method
        Local cChars, nBin
        cChars = ""
        For nDigit = 1 To Len(cBin)
          nBin = Asc(Substr(cBin, nDigit, 1))
          cChars = cChars + This.Hex2Char(Int(nBin/16)) + ;
            This.Hex2Char(Mod(nBin,16))
        EndFor
        Return(cChars)
      EndProc
      Protected Procedure hex2char(nHex)
        */ This method converts a hex value to  ASCII 
        Local nAsc
        Do Case
          Case Between(nHex,0,9)
            nAsc = 48 + nHex
          Case Between(nHex,10,15)
            nAsc = 65 + nHex - 10
        EndCase
        Return(Chr(nAsc))
      EndProc
      Procedure import(cString)
        */ This method takes the binary string and populates the 4 data
        */ properties
        With This
          .Data1 = Left(cString, Len(.Data1))
          cString = SubStr(cString, Len(.Data1)+1)
          .Data2 = Left(cString, Len(.Data2))
          cString = SubStr(cString, Len(.Data2)+1)
          .Data3 = Left(cString, Len(.Data3))
          cString = SubStr(cString, Len(.Data3)+1)
          .Data4 = Left(cString, Len(.Data4))
        EndWith
        Return cString
        EndProc
    
      Protected Procedure export
        */ This method creates the buffer to pass to the GUID API.
        With This
          .Data1 = Space(4)
          .Data2 = Space(2)
          .Data3 = Space(2)
          .Data4 = Space(8)
        EndWith
        Return(This.Data1 + This.Data2 + This.Data3 + This.Data4)
      EndProc
      Protected Procedure Convert(cGuid)
        */ This method makes the call to the BinToHex that 
        */ converts the data in the 4 data properties from 
        With This
          cGuid =  .BinToHex(.Data1) + "-" + .BinToHex(.Data2) + "-" + ;
            .BinToHex(.Data3) + "-" + .BinToHex(.Data4)
          Return cGuid
        Endwith 
        EndProc
      Procedure Init
        */ Declare the function in the DLL
        Declare Integer UuidCreate ;
          In C:\Winnt\System32\RPCRT4.DLL String @ UUID
        Return
      EndProc
    EndDefine
    

    Output is produced as follows:

    Disconnected/Persisted Recordsets

    One of the most powerful features of ADO is the ability to create both disconnected and persisted recordsets. A disconnected recordset is a client-side recordset that does not have a current ActiveConnection. SQL data sources, such as SQL Server, Oracle, and so on, are licensed according to the number of concurrent connections. For example, the number of people that using an application connected to SQL Server is 300. However, it has been determined that at any time, only 50 users actually use the services of a connection. A connection is needed only when data is being requested, updates are made, or a stored procedure on the database server is invoked. From a financial standpoint, it is far less expensive for a company to only purchase 50 licenses than to purchase 300. From a resource standpoint, performance should improve because the server only has the overhead of 50 connections instead of 300, of which 250 are idle at any time.

    Using the ADO recordset of customer data already created, the following code disconnects the client-side recordset:

    oRecordSet.ActiveConnection = Null
    

    If you attempt to do this with a server-side recordset, an error occurs stating that the operation is not allowed on an open recordset. Once the recordset is disconnected, you can continue to work with and modify records. The following code will work:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       ?oRecordset.Fields("companyname").Value
       oRecordset.Fields("companyname").Value = ;
          Upper(oRecordset.Fields("companyname").Value)
       oRecordset.MoveNext 
    EndDo
    

    With modified records in a client-side recordset, three basic options exist.

    • Cancel local changes
    • Marshall local changes to the server
    • Save (persist) the recordset locally.

    You can save (persist) the recordset locally for both later use and, ultimately, for marshalling those persisted changes back to the server.

    The first choice is pretty simple to implement, since it takes one line of code:

    oRecordset.CancelBatch 
    

    The second choice is also simple to implement. Much of the work in updating multiple records and transactions has already been detailed. This procedure really involves two separate steps:

    1. Re-establish an active connection.
    2. Marshal modified records back to the data source.

    The following code re-establishes the connection:

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
          ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    oRecordset.Activeconnection = oConnection
    

    Then the code marshals the records by attempting the updates

    Local Err,cOldErr
    cOldErr = On("error")
    On Error Err = .T.
    With oRecordset
       .ActiveConnection.BeginTrans
       .UpdateBatch
       If Err
          .ActiveConnection.RollBackTrans
          .CancelBatch
       Else
          .ActiveConnection.CommitTrans
       Endif
    EndWith   
       On Error &cOldErr
    

    Often, however, there’s a need to shut things down and then reopen the recordset at another time. To be effective, the recordset must reflect incremental changes. This cycle may repeat any number of times.

    To illustrate how to persist a recordset, consider again the following code that modifies records in a Recordset object:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       ?oRecordset.Fields("companyname").Value
       oRecordset.Fields("companyname").Value = ;
          Upper(oRecordset.Fields("companyname").Value)
       oRecordset.MoveNext 
    EndDo
    

    Now you can invoke the Save method to persist the recordset:

    oRecordset.Save("c:\temp\customers.rs")
    

    At a later time, you can open the persisted recordset:

    oRecordset = CreateObject("adodb.recordset")
    oRecordset.Open("c:\temp\customers.rs")
    

    After the persisted recordset is reopened, you can use the same code, which establishes a connection to a disconnected recordset, to make additional modifications. You can marshal changes made in the persisted recordset to the underlying data source.

    Hierarchical/Shaped Recordsets

    Visual FoxPro not only provides the ability to work with local data, but also the ability to set up relations using the Set Relation command. When you move the record pointer in the parent table, the record pointer automatically moves in any child tables that exist. This makes working with and building interfaces for one to many relationships very simple in Visual FoxPro. Fortunately, the same capability exists in ADO, in the form of hierarchical recordsets, also referred to as shaped recordsets.

    There are two necessary components when creating and working with hierarchical recordsets:

    • The Microsoft DataShape OLE DB Provider, MSDataShape
    • The Shape language, a superset of the SQL syntax

    The first requirement is fairly easy to fulfill because it only entails setting the Provider property of the ADO Connection object to the proper value:

    oConnection.Provider = "MSDataShape"
    

    The second requirement, using the Data Shape language, is a bit more challenging. When you first see Data Shape language, it can be fairly intimidating, just as FoxPro may have been when you first worked with it. But like anything else, with a bit of practice and patience, Microsoft Data Shape language will become second nature.

    To examine Shape language, consider a parent-child common scenario of customers and orders. For each customer, zero or more orders can exist. In turn, each order can contain one or more line items. The following code employs Shape syntax to relate customers and orders in the SQL Server Northwind database:

    SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND ({SELECT * 
    FROM "dbo"."Orders"} AS Orders RELATE "CustomerID" TO "CustomerID") AS 
    Orders
    

    If your first thought is, “Gee, this is like setting relations in Visual FoxPro,” you are indeed correct. It is exactly the same principle. If the Shape syntax is broken down, the task becomes manageable. The first clause in the code begins with the keyword SHAPE, to signify that what follows is not pure SQL, but rather, Data Shape language. The Data Shape language is a super-set of SQL, which is why you need to use MSDataShape as the OLE DB provider. MSDataShape can interpret and execute Shape commands. Finally, the last portion of the first command specifies that the results of the SQL statement are to be aliased as Customers.

    In the next set of commands, things get a bit complicated, especially when the hierarchy is nested an additional one or two levels (this is the case when order details are added, as we’ll do in the next example).

    You can interpret the keyword APPEND as “Append the results of the next SQL statement to the results of the previous SQL statement.” Of course, just appending records won’t suffice. Rather, you must provide a rule that specifies how the records are to be related. This is where the RELATE keyword comes into play.

    You can interpret the RELATE keyword as, “When appending records, do so based on these join fields.” In this case, the join is between the CustomerID column in the Customers table and the CustomerID column in the Orders table.

    Finally, we need to alias the data that was just appended as Orders. The following code sets up the objects and creates the hierarchical recordset:

    #Include adovfp.h
    Local oRecordset,oConnection,oCommand, cShpStr
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers '
    cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Orders"} ;
      AS  Orders '
    cShpStr = cShpStr + 'RELATE "CustomerID" TO "CustomerID") AS Orders'
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security ;
        Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    

    The question at this point is, “How is the data appended?” The technique is rather clever. When you append a recordset to another recordset, you do so through a Field object. If you query the Count property of the Fields collection, you discover that the value of 12 is returned. However, in SQL Server, you see that the Customers table only has 11 fields. The twelfth field, in this case, is actually a pointer to the Orders recordset. The rows in the Orders recordset for a given row in the Customers recordset are only those for that customer. The following code illustrates just how powerful hierarchical recordsets are:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       With oRecordset
          ?.Fields("Customerid").Value,.Fields("CompanyName").Value
       EndWith
       oOrders = oRecordset.Fields("orders").Value
       Do While !oOrders.Eof
          With oOrders
          ?Chr(9),.Fields("Customerid").Value,.Fields("orderdate").Value
          .MoveNext
          EndWith   
       EndDo   
       oRecordset.MoveNext
    EndDo
    

    With the basics of hierarchical recordsets out of the way, we can turn our attention to a more complicated, real-life example. The following example adds several dimensions to the recordset.

    First, the Order Details table is appended to the Orders child recordset. In this case, a new field that will in turn point to the OrderDetails recordset, is added to the Orders recordset. The Products table is then appended to the OrderDetails recordset providing three levels of nesting. Appended to the Products recordset are two tables, Categories and Suppliers. Traversing up the hierarchy to the Orders recordset appends the Employees table.

    This list illustrates the hierarchy and shows all the tables involved as well as the nesting scheme. When creating reports, it is quite possible that you will need all of these tables. The ability to relate tables in this fashion and the ability to display the data in a user interface or a report have always been true powers of Visual FoxPro. Before ADO, attempting all this work outside Visual FoxPro was extremely difficult, sometimes bordering on the impossible.

    Customers
    
    Orders
    
    OrderDetails
       Products
          Categories
          Suppliers
    Employees
       EmployeeTerritories
          Territories
             Region
    Shippers
    
    

    The following is the Shape syntax to create the hierarchical recordset:

    SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND (( SHAPE 
    {SELECT * FROM "dbo"."Orders"} AS Orders APPEND (( SHAPE {SELECT * FROM 
    "dbo"."Order Details"} AS OrderDetails APPEND (( SHAPE {SELECT * FROM 
    "dbo"."Products"} AS Products APPEND ({SELECT * FROM "dbo"."Categories"} 
    AS Categories RELATE 'CategoryID' TO 'CategoryID') AS Categories,({SELECT 
    * FROM "dbo"."Suppliers"} AS Suppliers RELATE 'SupplierID' TO 
    'SupplierID') AS Suppliers) AS Products RELATE 'ProductID' TO 
    'ProductID') AS Products) AS OrderDetails RELATE 'OrderID' TO 'OrderID') 
    AS OrderDetails,(( SHAPE {SELECT * FROM "dbo"."Employees"} AS Employees 
    APPEND (( SHAPE {SELECT * FROM "dbo"."EmployeeTerritories"} AS 
    EmployeeTerritories APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"} 
    AS Territories APPEND ({SELECT * FROM "dbo"."Region"} AS Region RELATE 
    'RegionID' TO 'RegionID') AS Region) AS Territories RELATE 'TerritoryID' 
    TO 'TerritoryID') AS Territories) AS EmployeeTerritories RELATE 
    'EmployeeID' TO 'EmployeeID') AS EmployeeTerritories) AS Employees RELATE 
    'EmployeeID' TO 'EmployeeID') AS Employees,({SELECT * FROM 
    "dbo"."Shippers"} AS Shippers RELATE 'ShipVia' TO 'ShipperID') AS 
    Shippers) AS Orders RELATE 'CustomerID' TO 'CustomerID') AS Orders
    

    This is just about as complicated as it gets. Nobody in their right mind would want to hammer this code out manually. Fortunately, there is a visual way to build this code. The DataEnvironment designer that ships with Visual Basic allows you to visually design ADO connections, recordsets, and hierarchical recordsets. The following illustrates how this hierarchical recordset appears in the designer:

    The extensive Shape syntax can be copied and pasted into Visual FoxPro, or any other environment that can host ADO. For complete details on how to use the DataEnvironment designer, consult the Visual Basic documentation on the MSDN CDs that ship with Visual Studio.

    The following Visual FoxPro code traverses the hierarchical recordset and displays the data:

    #Include adovfp.h
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"}  AS Customers APPEND'
    cShpStr = cShpStr + '(( SHAPE {SELECT * FROM "dbo"."Orders"}  AS Orders '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Order 
    Details"}  AS OrderDetails '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Products"}  
    AS Products '
    cShpStr = cShpStr  + 'APPEND ({SELECT * FROM "dbo"."Categories"}  AS 
    Categories '
    cShpStr = cShpStr  + 'RELATE "CategoryID" TO "CategoryID") AS 
    Categories,'
    cShpStr = cShpStr  + '({SELECT * FROM "dbo"."Suppliers"}  AS Suppliers '
    cShpStr = cShpStr  + 'RELATE "SupplierID" TO "SupplierID") AS Suppliers) 
    AS Products '
    cShpStr = cShpStr  + 'RELATE "ProductID" TO "ProductID") AS Products) AS 
    OrderDetails '
    cShpStr = cShpStr  + 'RELATE "OrderID" TO "OrderID") AS OrderDetails,'
    cShpStr = cShpStr  + '(( SHAPE {SELECT * FROM "dbo"."Employees"}  AS 
    Employees '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM 
    "dbo"."EmployeeTerritories"}  AS EmployeeTerritories '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"}  AS Territories '
    cShpStr = cShpStr  + 'APPEND ({SELECT * FROM "dbo"."Region"}  AS Region '
    cShpStr = cShpStr  + 'RELATE "RegionID" TO "RegionID") AS Region) AS 
    Territories '
    cShpStr = cShpStr  + 'RELATE "TerritoryID" TO "TerritoryID") AS 
    Territories) AS EmployeeTerritories '
    cShpStr = cShpStr  + 'RELATE "EmployeeID" TO "EmployeeID") AS 
    EmployeeTerritories) AS Employees '
    cShpStr = cShpStr  + 'RELATE "EmployeeID" TO "EmployeeID") AS Employees,'
    cShpStr = cShpStr  + '({SELECT * FROM "dbo"."Shippers"}  AS Shippers '
    cShpStr = cShpStr  + 'RELATE "ShipVia" TO "ShipperID") AS Shippers) AS 
    Orders '
    cShpStr = cShpStr  + 'RELATE "CustomerID" TO "CustomerID") AS Orders '
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security 
    Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    Do While !oRecordset.Eof
       With oRecordset
          ?.Fields("CustomerID").Value,.Fields("CompanyName").Value
       EndWith
       oOrders =  oRecordset.Fields("orders").Value
       Do While !oOrders.Eof
          oShippers = oOrders.Fields("shippers").Value
          oEmployee = oOrders.Fields("employees").Value
          oEmployeeTerritories = 
    oEmployee.Fields("employeeterritories").Value
          oTerritories = oEmployeeTerritories.Fields("territories").Value
          oRegion = oTerritories.Fields("region").Value
          ?"Order ID:  ",oOrders.Fields("orderid").Value,;
          "Order Date:  ",oOrders.Fields("orderdate").Value
          oOrderDetails = oOrders.Fields("orderdetails").Value
          ?"Territory:  ", 
    oTerritories.Fields("territorydescription").Value,;
          "Region:  ",oRegion.Fields("RegionDescription").Value
          ?"Shipper: ",oShippers.Fields("companyname").Value
          oEmployee = oOrders.Fields("employees").Value
          With oEmployee
             ?"Employee: ",.Fields("employeeid").Value,;
             .Fields("firstname").Value + " " + .Fields("lastname").Value
          EndWith   
          ?"Order Details:  "
          Do While !oOrderDetails.Eof
             oProducts = oOrderDetails.Fields("Products").Value
             oCategories = oProducts.Fields("categories").Value
             oSuppliers = oProducts.Fields("suppliers").Value
             ?Chr(9),;
              oProducts.Fields("productname").Value,;
              oSuppliers.Fields("companyname").Value,;
              oCategories.Fields("categoryname").Value,;
              oOrderDetails.Fields("Quantity").Value,;
              oOrderDetails.Fields("UnitPrice").Value
              oOrderDetails.MoveNext
          EndDo
          oOrders.MoveNext
       EndDo   
       oRecordset.MoveNext
    EndDo
    

    The output appears as follows:

    Because a hierarchy exists, the ability to create drill-down interfaces becomes a fairly simple task. The preceding Visual FoxPro code illustrates how to traverse the hierarchy.

    Perhaps you want to use Microsoft Word or Excel as a reporting tool. With a combination of Visual FoxPro COM servers, ADO, and Automation, the process becomes manageable. The first and third parts of the solution have been around. However, only now that a set of COM objects exists to handle and work with data as Visual FoxPro does natively can the solution become a reality.

    Hierarchical recordsets and recursive relationships

    One of the nice features of SQL Server, and of most other server back ends is provision for recursive relations. The following is the SQL Server 7.0 database diagram for the Northwind database:

    In the Northwind database, the Employees table employs recursion to support a manager/staff relationship. Both managers and staff are employees. In some cases, it happens that some employees report to other employees. In Visual FoxPro, you can create the same sort of relation by opening a table twice using two different aliases. In ADO, the task is totally supported and is quite easy to implement. The following is the Shape syntax:

    SHAPE {SELECT * FROM "dbo"."Employees"}  AS Managers APPEND ({SELECT * 
    FROM "dbo"."Employees"}  AS Staff RELATE 'EmployeeID' TO 'ReportsTo') AS 
    Staff
    

    The following Visual FoxPro code displays a list of managers and the staff that reports to each manager:

    #Include adovfp.h
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Employees"}  AS Managers '
    cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Employees"} AS Staff '
    cShpStr = cShpStr + 'RELATE "EmployeeID" TO "ReportsTo") AS Staff '
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security 
    Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    Do While !oRecordset.Eof
       oStaff = oRecordset.Fields("staff").Value
       If oStaff.Recordcount > 0
          With oRecordset
             ?.Fields("firstname").Value + " " + ;
             .Fields("lastname").Value ,;
             .Fields("Title").Value
             Do While !oStaff.Eof
                With oStaff
                   ?Chr(9),;
                   .Fields("firstname").Value + " " + ;
                   .Fields("lastname").Value ,;
                   .Fields("Title").Value
                EndWith
                oStaff.MoveNext
             EndDo
          EndWith
       Endif
       oRecordset.MoveNext
    EndDo
    

    The output appears as follows:

    Finally, note that hierarchical recordsets are updateable. The following code expands the previous example to illustrate how to make a simple update:

    Do While !oRecordset.Eof
       oStaff = oRecordset.Fields("staff").Value
       If oStaff.Recordcount > 0
          With oRecordset
             Do While !oStaff.Eof
                With oStaff
                   .Fields("firstname").Value = ;
                      Upper(.Fields("firstname").Value)
                   .Fields("lastname").Value = ;
                      Upper(.Fields("lastname").Value)
                   .Fields("Title").Value = ;
                      Upper(.Fields("Title").Value)
                EndWith
                oStaff.MoveNext
             EndDo
             */ Write changes to Staff recordset
             oStaff.UpdateBatch
          EndWith
       Endif
       oRecordset.MoveNext
    EndDo
    

    The ability to view related records, coupled with the ability to make updates, places the ADO hierarchical recordset capability on par with similar capabilities in Visual FoxPro.

    Multiple recordsets

    Use of hierarchical recordsets represents only one method for returning data from multiple recordsets in one object. For starters, building hierarchical recordsets is not the most straightforward of propositions. In many cases, a simpler alternative may be all that is required.

    Consider the case where you need a specific customer record and the orders for that customer. Yes, you could use a hierarchical recordset. But, there is a simpler way: run two SQL statements.

    Some OLE DB providers can process multiple SQL Statements. The OLE DB Provider for SQL Server has this capability. Attempting to do this with Visual FoxPro tables via the OLE DB Provider for ODBC will not work.

    When using this technique, you have two choices on where the logic exists to perform the task. One choice is to build the SQL on the client and pass it to the server through a Command object. The other choice is to invoke a stored procedure on the database server through a Command object. I’ll illustrate both techniques. The Command object will be discussed in detail later in this paper.

    To illustrate the stored procedure method, the following stored procedure must be created on the SQL Server Northwind database:

    CREATE  PROCEDURE CustomerAndOrders @CustomerID nchar(5)
    AS
    Select * From Customers Where Customers.CustomerID = @CustomerID
    Select * From Orders Where Orders.CustomerID = @CustomerID 
    

    With the stored procedure created, the following code will create the recordset:

    #Include adovfp.h
    oConnection = CreateObject("adodb.connection")
    oCommand = CreateObject("adodb.command")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = ;
          "Persist Security Info=False;User ID=sa;Initial
            Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oCommand
       .CommandText = "CustomerAndOrders"
       .ActiveConnection = oConnection 
       .CommandType = adCmdStoredProc 
    EndWith
    oCommand.Parameters("@CustomerID").Value = "ALFKI"
    oRecordset = oCommand.Execute
    Do While !oRecordset.Eof
       ?oRecordset.Fields(1).Value
       oRecordset.MoveNext
    EndDo
    oRecordset = oRecordset.NextRecordset
    Do While !oRecordset.Eof
       ?oRecordset.Fields(0).Value
       oRecordset.MoveNext
    EndDo
    

    Like any recordset, the recordset just produced can be navigated. Once the first set of records from the Customers table have been navigated, the NextRecordset method is invoked. This causes the recordset produced by the second SQL statement to become available. Thus, the next set of commands loops through the records from the Orders table. This technique is ideal in those situations where you may need to populate Combo or ListBox controls.

    The previous example references a collection that has not been discussed yet, the Parameters collection. The Parameters collection and the individual Parameter objects that it contains serve several purposes. One purpose is to provide the capacity to create parameterized queries. Another purpose is to provide the ability to send arguments to, and return data from, a stored procedure. For more information on the Parameters collection, see the Command Object section of this paper.

    Alternatively, you can produce the SQL on the client if you wish. The following code illustrates the difference:

    With oCommand
       .CommandText = "Select * From Customers Where CustomerID =
          'ALFKI'" + Chr(13) + "Select * From Orders Where CustomerID =
          'ALFKI'"
       .ActiveConnection = oConnection 
       .CommandType = adCmdText 
    EndWith
    oRecordset = oCommand.Execute
    

    The same result is achieved. The difference lies in how the result is achieved.

    Which approach is better?

    It depends on what your requirements are. The first option, which uses stored procedures, is more secure; the code is set and you can assign permissions with regard to who can execute the stored procedure. The second option provides more flexibility, but less security.

    Fabricated recordsets

    Up to this point, recordset objects have been presented in the context of origination from an ADO connection. In many cases, you may want to create an ADO recordset with data that does not come from a data source, just as you may in some cases use the Create Cursor command in Visual FoxPro. For example, you may have an application that works with a small amount of data, such as an array or Visual FoxPro cursor. Perhaps you need to dynamically build a table structure. Whatever the reason, the ability to create ADO recordsets from scratch is powerful.

    To illustrate this capability, consider the need to fetch a list of files from a specified directory. In Visual FoxPro, a handy function, ADIR( ), performs this sort of task. However, what if you need to pass the data to another application? Or, perhaps you need to persist the list to a file on disk. While Visual FoxPro arrays are powerful, ADO recordsets provide a compelling alternative. The following code fetches a list of files from a specified directory, fabricates a recordset, and copies the values from the array into the newly created recordset:

    */GetFiles.prg
    #INCLUDE "adovfp.h"
    Local Array aFiles[1]
    Local nFiles,nField,nFile,oRS
    nFiles = Adir(aFiles,Getdir( )+"*.*")
    oRS=Createobject("adodb.recordset")
    With oRS
    .CursorLocation=ADUSECLIENT
    .LockType=ADLOCKOPTIMISTIC
    */ Adding new fields is a matter of appending
    */ new field objects to the Fields Collection. 
    .Fields.Append("File",ADCHAR,20)
    .Fields.Append("Size",ADDOUBLE,10)
    .Fields.Append("DateTime",ADDBTIME,8)
    .Fields.Append("Attributes",ADCHAR,10)
    .Open
    EndWith
    For nFile = 1 To nFiles
       */ Add a new record. This automatically makes
       */ the new record the current record - just
       */ like VFP.
       oRS.AddNew
       With ors
          .Fields("File").Value = aFiles[nFile,1]
          .Fields("Size").Value = aFiles[nFile,2]
          .Fields("DateTime").Value = ;
            Ctot(Dtoc(aFiles[nFile,3]) + " " + aFiles[nFile,4])
          .Fields("Attributes").Value = aFiles[nFile,5]
       EndWith
    Next nItem
    Return oRS
    

    With the new recordset created and populated, it can be navigated like any other recordset:

    oFiles = GetFiles ( )
    Do While !oFiles.Eof
       ?oFiles.Fields("File").Value
       oFiles.movenext
    EndDo
    

    ADO recordsets instead of arrays

    Referring to the previous example, let’s say that the list needs to be sorted by file size, descending. Arrays in Visual FoxPro can be sorted, when all columns in the array are of the same data type. In this case, there are three data types: Character, Numeric, and DateTime. With a client-side ADO recordset, the process becomes simple. The following code does the trick:

    oRS.Sort = "Size Desc"
    

    Sorts are not limited to just one column. Perhaps you need to sort by size, descending, and then by file, ascending:

    oRS.Sort = "Size Desc,File"
    

    And, when it comes to sorting, such properties as Bookmark and AbsolutePosition that have already been demonstrated are available here as well.

    Perhaps you need to find a specific value. The ASCAN( ) function in Visual FoxPro enables you to do this. However, it does not allow you to specify a particular column to search. Rather, once the first occurrence of a specified value is found, regardless of the column, the search is stopped. With ADO recordsets, more granular control is provided. The following code checks to see if a file called VFP6.EXE is in the recordset:

    oRS.Find("File Like 'VFP6.EXE'")
    If !oRS.Eof
       */ Found it
    Else
       */ Not found
    Endif
    

    Finally, you may wish to filter the list based on the file size being greater than a specified value:

    oRS.Filter = "size > 50000"
    

    When evaluating the tools at your disposal for local data handling, be sure to consider fabricated ADO recordsets. Also, if you find yourself running into obstacles with Visual FoxPro arrays, fabricated ADO recordsets may provide a sound alternative.

    Command Object

    ProgID: ADODB.Command

    The purpose of the Command object is just as the its name implies, to run commands. For example, you may need to run a SQL update against a SQL Server table. To illustrate, the following code applies a 10 percent increase in the UnitPrice field in the Products table of the SQL Server Northwind database:

    oCommand = CreateObject("adodb.command")
    With oCommand
       .ActiveConnection = oConnection
       .CommandText = "Update Products Set unitprice = unitprice * 1.1"
       .Execute
    EndWith
    

    The ActiveConnection property

    To review, both the Command object and Recordset object have the ActiveConnection property. A Command object needs to know what data source it is to execute commands against. A Recordset object needs to know what data source contains the data it is to retrieve. The way you accomplish this is by setting the ActiveConnection property.

    The ActiveConnection property presents a great opportunity to talk about the flexible nature of the ADO object model. The ADO object model is very flat, in that you do not have to create a series of objects in order to gain access to other objects. For example, the following is one way to create and open both a Connection and a Recordset object:

    oConnection = CreateObject(""adodb.connection"")
    oRecordset = CreateObject(""adodb.recordset"")
    With oConnection
       .Provider = ""SQLOLEDB.1""
       .ConnectionString = ""Persist Security Info=False;User 
        ID=sa;Initial Catalog=Nothwind;Data Source=JVP""
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = ""Products""
       .Open
    EndWith
    

    Here is another way to create the two objects:

    oRecordset = CreateObject(""adodb.recordset"")
    With oRecordset
       .ActiveConnection = ""Provider=SQLOLEDB.1;Persist Security 
        Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP""
       .Source = ""Products""
       .Open
    EndWith
    

    Now, you can reference the Connection object because it has been implicitly created from the passed connection string:

    ?oRecordset.ActiveConnection.ConnectionString
    

    The same is true for the Command object. While a Command object was not explicitly created, a Command object was in fact created and actually did the work of creating the recordset. Using the recordset just created, the following command will yield “Products” as the CommandText:

    ?oRecordset.ActiveCommand.CommandText
    

    Which method should you use?

    It is really a matter of preference. The latter method, which uses only the RecordSet object, is somewhat overloaded. It carries the same overhead as the former method because you must still create a Connection object. The former method is probably a better way to go as it makes for more readable code.

    Parameters collection

    The Parameters collection works with the Command object. The primary use of the Parameters Collection is to both pass arguments to, and accept return values from stored procedures. To illustrate, consider the CustOrderHist stored procedure in the SQL Server Northwind database:

    CREATE PROCEDURE CustOrderHist @CustomerID nchar(5)
    AS
    SELECT ProductName, Total=SUM(Quantity)
    FROM Products P, [Order Details] OD, Orders O, Customers C
    WHERE C.CustomerID = @CustomerID
    AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND 
    OD.ProductID = P.ProductID
    GROUP BY ProductName
    

    To illustrate how the Parameters collection is used in conjunction with the Command object, consider the following comprehensive example:

    First, you need to establish a valid connection:

    oConnection = CreateObject("adodb.connection")
    

    Next, the connection needs to be opened.

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    

    With a valid, open connection, a Command object can be prepared:

    With oCommand
       .ActiveConnection = oConnection 
       .CommandText = "CustOrderHist"
       .CommandType = adCmdStoredProc && adCmdStoredProc = 4
    EndWith
    

    At this point, information can be obtained from the Parameters collection:

    For Each Parameter in oCommand.Parameters
       ?Parameter.Name,Parameter.Size,Parameter.Type
    Next Parameter
    

    The first Parameter object is reserved for the value that the stored procedure may return. Regardless of whether the stored procedure explicitly returns a value, this Parameter object will be created. Examining the CustOrderHist stored procedure, note that a single argument, a customer ID, is accepted.

    With a Command object and Parameter object in place, the real work can begin. To get things rolling, a value needs to be assigned to the Parameter object that will in turn be passed to the stored procedure. In this case, a SQL statement is executed that totals the quantity, by product, that a specified customer has purchased. The following code provides a customer ID and executes the stored procedure:

    oCommand.Parameters("@CustomerID").Value = "ALFKI"
    oRecordset = oCommand.Execute
    

    Yet another way to produce a Recordset object is through the execution of a stored procedure. The resulting Recordset object contains two fields that correspond to the select statement in the CustOrderHist stored procedure. Need a different history? Just update the Value property of the Parameter object and invoke the Execute method of the Command object.

    The Parameters collection also comes into play in the area of parameterized queries. Consider the following SQL Statement:

    Select * ;
       From Customer ;
       Where country = ? And max_order_amt > ?
    

    As with views, either local or remote, in Visual FoxPro, so too can queries be parameterized in ADO. In ADO, the question mark acts as a placeholder for parameters. The following example illustrates how to put this all together.

    First, a connection and a Command object need to be created:

    oConnection = CreateObject("adodb.connection")
    oCommand = CreateObject("adodb.command")
    

    Next, the connection needs to be established:

    oConnection.Open("northwind","sa","")
    

    For illustration purposes, the OLE DB Provider for ODBC is used. The native OLE DB Provider for SQL Server could have been used as well.

    Next, the Command object needs to be prepared:

    With oCommand
       .ActiveConnection = oConnection
       .CommandText = "Select * From Customer Where country = ? 
    EndWith
    

    With the Command object ready to go, a parameter object needs to be created:

    oCountryParameter = ;
     oCommand.CreateParameter("country",adChar,adParamInput,1," "))
    

    The arguments for the CreateParameter method are as follows:

    • Name—The name of the parameter.
    • Type—The data type of the parameter. A list of valid values is contained in DataTypeEnum.
    • Direction—The direction of the parameter. Parameters sent to a command are input parameters. Arguments passed back from a command are output parameters. A list of valid values is contained in ParameterDirectionEnum.
    • Size—The length of the parameter.
    • Value—The initial value of the parameter.

    Alternatively, the parameter could have been created like this:

    OCountryParameter = CreateObject("adodb.parameter")
    With oCountryParameter
       .Name = "Country"
       .Type = adChar
       .Direction = adParamInput
       .Size = 1
       .Value = " "
    EndWith
    

    Once the parameter has been created, it needs to be appended into the Parameters collection of the Command object:

    oCommand.Parameters.Append(oCountryParameter)
    

    With the parameter in place, the value of the parameter can be set. In this case, the parameter will be set so that any country that begins with the letter U will be returned into a Recordset object:

    With oCountryParameter
       .Size = 2
       .Value = "U%"
    EndWith
    

    Now, a Recordset object can be created:

    oRecordset = oCommand.Execute
    

    A useful feature of specifying parameters is that this enforces characteristics such as size, data type, and so on. For example, the preceding parameter was defined as a character. If a value based on a different data type was assigned to the Value property of the Parameter object, an error would result. The same is true if the assigned value is greater in length than what has been specified by the Size property.

    Finally, if a list of customers in Mexico were required, the following code would complete the task:

    With oCommand
       .Parameters("country").Size = Len("Mexico")
       .Parameters("country").Value = "Mexico"
       oRecordSet = .Execute 
    EndWith
    

    Properties Collection

    Recall the earlier assertion that, by itself, ADO is incapable of doing anything? ADO in fact just provides an interface. OLE DB providers give ADO the ability to do anything. So then, what distinguishes one OLE DB provider from another? More specifically, how can you determine what an OLE DB provider can and cannot do, or what attributes it does or does not possess? Depending on the OLE DB provider you use, or the type of recordset you use (client or server), what is supported will likely differ.

    The Properties collection applies to the ConnectionRecordset, and Field objects. The Command object also has a Properties collection, which is identical to the Recordset object Properties collection.

    Multiple result sets provide a good example of varying OLE DB provider support. To determine if multiple result sets can be obtained, you can refer to the “Multiple Results” properties:

    If oConnection.Properties("Multiple Results").Value = 1
       */ Supports multiple result sets
    EndIf
    

    While the OLE DB providers for SQL Server and ODBC both support multiple results, the OLE DB provider for Jet does not. To illustrate, the following is valid syntax for SQL Server:

    oRecordset.Source="SELECT * FROM customers;"+"SELECT * FROM orders"
    oRecordset.Open
    ?oRecordSet.Fields.Count && number of fields in customers table
    oRecordset = oRecordset.NextRecordSet
    ?oRecordSet.Fields.Count && number of fields in orders table
    

    In this case, the OLE DB Provider for SQL Server can return multiple recordsets. If you attempt the same thing with the OLE DB Provider for ODBC, which you need to use when accessing Visual FoxPro data, you will receive an error message stating that the requested action is not supported by the OLE DB provider.

    Another example involves the way in which the Properties collection deals with the location of a Recordset object. Recordsets can either exist locally as client-side recordsets or they can exist remotely as server-side recordsets. Client-side recordsets, as will be discussed shortly, have several capabilities that server-side recordsets do not have. One of these abilities is to create indexes. The following code creates a client-side recordset:

    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .Cursorlocation = adUseClient && adUseClient = 3
       .ActiveConnection = oConnection
       .Source = "Products"
       .Open 
    EndWith
    

    Now, lets create an index on the ProductName field using the following code:

    oRecordSet.Fields("productname").Properties("optimize").Value = .T.
    

    In the absence of a declaration of where a Recordset object should reside, the Recordset object, by default, resides on the server. Attempting to reference the Optimize property results in an error stating that the specified property could not be found in the collection.

    While the ADO interface is constant, depending on the provider you use, the capabilities may be very different. Be sure to consult your provider’s documentation.

    Remote Data Services

    One of the most powerful data access capabilities introduced by Microsoft is Remote Data Services (RDS). Although a separate set of objects exists for RDS, RDS is really just another component for use with ADO. There are two ways you can implement RDS.

    • Use the same ADO objects described in this paper
    • Use the RDS data control

    Let’s discuss the RDS data control option first, since it represents some uncharted territory.

    The RDS Data Control

    The following code creates an instance of the RDS data control:

    oRDSDataControl = Createobject("rds.datacontrol")
    

    Once the data control is created, only three properties need to be populated: ServerConnect, and SQL.

    With oRDSDataControl
       .Server = "http://jvp"
       .Connect = ;
        "Remote Provider=SQLOLEDB.1;database=northwind;User ID=sa;"
       .Sql = "Customers"
    EndWith
    

    Because we’re using the SQL Server OLE DB Provider, the SQL property can consist of just the table name. The following code retrieves the same recordset, but does so with the OLE DB provider for ODBC:

    With oRDSDataControl
       .Server = "http://jvp"
       .Connect = "dsn=northwind;uid=sa;pwd=;"
       .Sql = "Customers"
    EndWith
    

    Whenever possible, you should use a native OLE DB provider rather than the OLE DB provider for ODBC.

    With the RDS data control properties set, you can create a recordset. Invoke the Refresh method to accomplish this, as in the following code:

    oRDSDataControl.Refresh
    oRecordset = oRDSDataControl.Recordset
    

    From this point on, you can work with the recordset the same way you work with any other ADO client-side recordset:

    Do While !oRecordset.Eof
       orecordset.Fields(1).value = ;
          Proper(orecordset.Fields(1).value)
       oRecordset.Movenext
    EndDo
    oRecordset.Updatebatch
    

    Alternatively, you can replace the last line of code with a call to the SubmitChanges method of the RDS data control:

    oRDSDataControl.SubmitChanges
    

    Implementing RDS Through the ADO Interface

    You can invoke RDS by using the same ADO Connection object discussed above. As with hierarchical recordsets, the first step involves the selection of an OLE DB provider. In this case, the MSRemote provider is required. The following code sets up the Connection object:

    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "MS Remote.1"
    
       .ConnectionString = "Remote Server=http://jvp;Remote 
             Provider=SQLOLEDB.1;database=northwind;User ID=sa;Pwd=;"
       .Open
    EndWith
    

    The ADO ConnectionString property supports only four arguments. The first two, Provider and File Name, have already been discussed. The third and fourth, Remote Provider and Remote Server, are used by the RDS in the example above. The Remote Provider is the same OLE DB provider used when you create local connections. The additional parameters that specify the database, user ID, and password are used by the OLE DB Provider for SQL Server that in turn is located on the remote server. The following code connects the Recordset object and Connection object and with one difference, is basically the same as the previous examples in this paper:

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Customers"
       .LockType = adLockBatchOptimistic
       .Open
    EndWith
    

    The only difference is that properties such as CursorLocation and CursorType are omitted since all recordsets created through RDS must exist on the client. Additionally, all client-side recordsets are static types. If you like, you can still specify the properties explicitly. Any incompatible properties will be coerced to a valid value. For example, if you specify the CursorType to be a ForwardOnly cursor and you specify the recordset exists on the client, when the Open method is fired, ADO forces the cursor type to be static. The same is true if you specify the CursorLocation to be on the server and you use the MSDataShape provider. Since all hierarchical pecordsets must exist on the client, the CursorLocation is coerced to the proper value.

    Summary

    The goal of this paper has been to provide you with a fairly comprehensive overview of both ADO and RDS from the perspective of Visual FoxPro applications. Note that ADO is not a replacement for the Visual FoxPro Cursor Engine. Rather, regard it as another tool at your disposal. Both Visual FoxPro cursors and ADO recordsets have their relative strengths and weaknesses.

    ADO is ideal in situations where your application is component based, or in situations where you need to pass data to other applications such as Excel in automation operations. Fabricated ADO recordsets can provide an interesting alternative to arrays when more robust data handling requirements are necessary.

    For most local data handling operations however, Visual FoxPro cursors will usually provide better results.

    John V. Petersen, MBA, is president of Main Line Software, Inc., based in Philadelphia, Pennsylvania. John’s firm specializes in custom software development and database design. He is a Microsoft Most Valuable Professional and has spoken at many developer events, including Visual FoxPro Developers Conference, FoxTeach, the Visual FoxExpress Developer’s Conference, DevDays, and TechEd. In addition, John has written numerous articles for FoxTalk and FoxPro Advisor. John is co-author of Visual FoxPro 6 Enterprise Development and Hands-on Visual Basic 6—Web Development, both from Prima Publishing. John’s latest project is the ADO Developer’s Handbook, from Sybex Publishing, due September 1999.

    Customizing Visual FoxPro 6.0 Application Framework Components 

    • Article
    • 06/30/2006

    In this article

    1. Overview
    2. Examining Framework Components
    3. Designating the Classes You Want
    4. Specifying Your Own Framework Components

    Show 7 more

    Lisa Slater Nicholls

    October 1998

    Summary: Describes how the Microsoft® Visual FoxPro® version 6.0 Application Framework, including the Application Wizard and Application Builder, can be used by the beginning developer to turn out polished applications and customized by the more experienced developer to create more detailed applications. (32 printed pages)

    Contents

    Overview Examining Framework Components Designating the Classes You Want Specifying Your Own Framework Components A Closer Look at the Standard Application Wizard A New Application Wizard A Few Parting Thoughts about Team Practices Appendix 1 Appendix 2 Appendix 3 Appendix 4Expand table

    Click to copy the appfrmwk sample application discussed in this article.

    Overview

    The Visual FoxPro 6.0 Application Framework offers a rapid development path for people with little experience in Visual FoxPro. With a few simple choices in the Application Wizard and the Application Builder, beginning developers can turn out polished and practical applications.

    Under the hood, however, the framework offers experienced developers and teams much more. This article shows you how to adapt the framework components so they fit your established Visual FoxPro requirements and practices.

    In the first section of this article you’ll learn about the files and components that support the framework and how they work together while you develop an application. This information is critical to moving beyond simply generating framework applications to experimenting with framework enhancements.

    The second section teaches you how to apply your experiences with the framework to multiple applications. After you’ve experimented with framework enhancements for a while, you will want to integrate your changes with the framework, for standard use by your development team. By customizing the files the Application Wizard uses to generate your application, you’ll make your revisions accessible to team members—without sacrificing the framework’s characteristic ease of use.

    Examining Framework Components

    This section shows where the framework gets its features and components, and how these application elements are automatically adjusted during your development process.

    Once you see how and where framework information is stored, you can begin to try different variations by editing the versions generated for a framework application. When you’re satisfied with your changes, you can use the techniques in the next section to migrate them to your team’s versions of the framework components.

    Note   Like most Visual FoxPro application development systems, the framework is composed of both object-oriented programming (OOP) class components and non-OOP files. This distinction is important because you adapt these two types of components in different ways; classes can be subclassed, while non-OOP files must be included as is or copied and pasted to get new versions for each application. The framework is minimally dependent on non-OOP files, as you’ll see here, but these files still exist.

    Throughout this article we’ll refer to the non-OOP framework files as templates, to distinguish these components from true classes.

    Framework Classes

    The Visual FoxPro 6.0 framework classes are of two types:

    1. Framework-specific classes. These classes have been written especially for the application framework and provide functionality specific to the framework. The standard versions of these classes are in the HOME( )+ Wizards folder, in the _FRAMEWK.VCX class library.
    2. Generic components. These features come from class libraries in the HOME( )+ FFC (Visual FoxPro Foundation Classes) folder.

    _FRAMEWK.VCX

    The _FRAMEWK.VCX class library (see Figure 1) contains all the classes written specifically to support the framework. Each framework application you create has an application-specific VCX containing subclasses of the _FRAMEWK.VCX components. The Application Wizard puts these subclasses in a class library named <Your projectname> plus a suffix to designate this library as one of the wizard-generated files. To distinguish these generated, empty subclasses, it adds a special prefix to the class names as well.

    Figure 1. _FRAMEWK.VCX framework-specific class library, as viewed in Class Browser, is found in the HOME( )+ Wizards folder.

    Framework superclass: _Application

    The _Application class is a required ancestor class, which means that this class or a subclass of this class is always required by the framework. This class provides application-wide manager services. For example, it manages a collection of modeless forms the user has opened.

    You designate a subclass of _Application simply by using CREATEOBJECT( ) or NEWOBJECT( ) to instantiate the subclass of your choice. (By default, the framework provides a main program to do this, but this PRG contains no required code.) When your designated _Application subclass has instantiated successfully, you call this object’s Show( ) method to start running the application.

    Note   In this article, we’ll refer to the object you instantiate from a subclass of _Application as the application object. We’ll continue to refer to “your subclass of _Application” to mean the class definition instantiating this object, which will be in a VCX belonging to your application (not _FRAMEWK.VCX). You’ll also see references to “_Application“, that refer specifically to code and properties you’ll find in the superclass located in _FRAMEWK.VCX.

    At run time, the application object instantiates other objects as necessary to fill all the roles represented by the other classes in _FRAMEWK.VCX except _Splash. The framework identifies these roles as important to various application functions, but, as you’ll see in this section, you have full control over how the roles are carried out.

    Note   The _Splash class is an anomaly in _FRAMEWK.VCX; it isn’t instantiated or used by the framework application directly. (If it were instantiated by the application object, your splash screen would appear too late to be useful.) Instead, _Splash merely provides a default splash screen with some of the same attributes as _Application (for example, your application name and copyright). The Application Builder transfers these attributes to your application’s subclass of _Splash at the same time it gives them to your application’s subclass of _Application, so they stay synchronized. The default main program delivered with a framework gives you one way to instantiate this splash screen before you instantiate your application object.

    You certainly don’t need to use the method shown in the default main program for your splash screen. In fact, many applications do not need a splash screen at all. For those that do, you may prefer to use the Visual FoxPro –b<file name> command-line switch, which displays a bitmap of your choice during startup, rather than a Visual FoxPro form of any description.

    Framework superclass: _FormMediator

    You’ll grasp most of the “roles” played by the subsidiary classes in _FRAMEWK.VCX easily, by reading their class names and descriptions. (If you can’t read the full class description when you examine _FRAMEWK.VCX classes in a project, try using the Class Browser.) However, you’ll notice a _FormMediator class whose purpose takes a little more explaining.

    You add an object descended from the _FormMediator custom class to any form or form class, to enable the form to communicate efficiently with the application object. This section will show you several reasons the form might want to use services of the application object. With a mediator, your form classes have access to these services, but the forms themselves remain free of complex framework-referencing code.

    The _FormMediator class is low-impact. It doesn’t use a lot of resources, and its presence will not prevent your forms from being used outside a framework application. Using this strategy, the framework can manage any forms or form classes your team prefers to use, without expecting them to have any special inheritance or features.

    Like _Application, _FormMediator class is a required ancestor class. You can create other mediator classes, as you can subclass _Application to suit your needs, but your mediators must descend from this ancestor.

    We’ll refer to _FormMediator and its descendents as the mediator object, because (strictly speaking) your forms will see it as the “application mediator” while the application object treats it as a “form mediator.”

    The Visual FoxPro 6.0 Form Wizards create forms designed to take advantage of mediators when the framework is available. You can see some simple examples of mediator use in the baseform class of HOME( )+ Wizards\WIZBASE.VCX.

    Examine _FormMediator‘s properties and methods, and you’ll see that you can do much more with the mediator in your own form classes. For example, the application object calls mediator methods and examines mediator properties during its DoTableOutput( ) method. (This method allows quick output based on tables in the current data session.) Your mediator for a specific form could:

    • SELECT a particular alias to be the focus of the output.
    • Prepare a query specifically for output purposes (and dispose of it after the output).
    • Inform the application object of specific classes and styles to be used by _GENHTML for this form.
    • Change the output dialog box caption to suit this form.

    The mediator also has methods and properties designed to specify context menus for the use of a particular form. If the application object receives this information from the mediator, it handles the management of this menu (sharing it between forms as necessary).

    You’ll find one example of mediator use in the ErrorLogViewer class. (This use is described in Appendix 1, which covers the options system.) A full discussion of the _FormMediator class is beyond the scope of this document. The more information you give a mediator or mediator subclass, however, the more fully your forms can use framework’s features, without making any significant changes to the forms themselves.

    Note   The _Application class includes a property, lEnableFormsAtRuntime (defaulting to .T.), which causes the application object to add mediators at run time to any form not having a mediator of its own. You can specify the mediator subclass that the application adds to a form at run time. Keep in mind, however, that mediators added at design time will have a more complete relationship with their form containers, because these forms can include code referencing their mediator members. During a form’s QueryUnload event, for example, the form can use the mediator to determine whether the form contains any unconfirmed changes. Without code in the form’s QueryUnload method, the mediator can’t intercede at this critical point.

    Additional _FRAMEWK.VCX classes

    The other classes in _FRAMEWK.VCX are all dialog box and toolbar classes to perform common functions within an application. None of these classes are required ancestors; you can substitute your own user interfaces and class hierarchies for these defaults at will. Two of them (_Dialog and _DocumentPicker) are abstract; that is, they are never instantiated directly, existing only to provide properties and methods to their descendent classes. Others will not instantiate unless you pick specific application characteristics. For example, if you don’t write “top form” applications (MDI applications in their own frames) you will never use _TopForm, the _FRAMEWK.VCX class that provides the MDI frame window object.

    Once you have examined these classes, and identified their roles, you will know which ones supply the types of services you need in applications you write—and, of these, you will identify the ones you wish to change.

    Designating the Classes You Want

    For each class role identified by the framework, the application object uses corresponding xxxClass and xxxClassLib properties to determine the classes you want. To change which class is instantiated for each role, you change the contents of these properties in your subclass of _Application.

    For example, _Application has cAboutBoxClass and cAboutBoxClassLib properties, and it uses these properties to decide what dialog box to show in its DoAboutBox( ) method (see Figure 2).

    Figure 2. Class and ClassLib property pairs in the _Application object

    If you fill out a class property but omit the matching Classlib property, _Application assumes that your designated class is in the same library as the _Application subclass you instantiated. If your _Application subclass is in the MyApplication.vcx and cAboutBoxClass has the value “MyAboutBox” but cAboutBoxClassLib is empty, a call to the Application object’s DoAboutBox( ) method instantiates a class called MyAboutBox in MyApplication.vcx.

    If you call the method instantiating one of the subsidiary classes when the matching class property is empty, _Application attempts to provide appropriate behavior to the specific situation. For example, if the cAboutBoxClass property is empty, DoAboutBox( ) will simply do nothing, because it has no alternative. By contrast, if the cErrorViewerClass property is empty, the _Application DisplayErrorLog( ) method will ask its cusError member object to use its default error log display instead.

    Except for the cMediatorClass and cMediatorClassLib properties, which must specify a class descending from _FormMediator in _FRAMEWK.VCX, remember that there are no restrictions on these dialog boxes and toolbars. You don’t have to subclass them from the classes in _FRAMEWK.VCX, or even follow their examples, in your own classes fulfilling these framework roles.

    Even when you design completely different classes, you will still benefit from investigating the defaults in _FRAMEWK.VCX, to see how they take advantage of their relationship with the framework. For example, all the classes descended from _Dialog have an ApplyAppAttributes( ) method. When the framework instantiates these classes, it checks for the existence of this method. If the ApplyAppAttributes( ) method exists, the application object passes a reference to itself to the form, using this method, before it calls the Show( ) method. In this way, the dialog box can derive any framework-specific information it needs before it becomes visible. For instance, the About Box dialog box might adjust its caption using the _Application.cCaption property.

    If the ApplyAppAttributes( ) method does not exist in yourcAboutBoxClass class, no harm is done. The _Application code still tries to harmonize your dialog box with its interface, in a limited way, by checking to see whether you’ve assigned any custom value to its Icon property. If you haven’t, _Application assigns the value in its cIcon property to your dialog box’s icon before calling its Show( ) method.

    Note   This strategy typifies the framework’s general behavior and goals:

    • It tries to make the best use of whatever material you include in the application.
    • When possible, it does not make restrictive assumptions about the nature of this material.
    • It avoids overriding any non-default behavior you may have specified.

    Investigating the default _Options dialog box class and _UserLogin default dialog boxes will also give you insight into the _Application options and user systems. While the dialog boxes themselves are not required, you will want to see how they interact with appropriate _Application properties and methods, so your own dialog boxes can take advantage of these framework features. In particular, the _Application options system has certain required elements, detailed in Appendix 1.

    FoxPro Foundation Generic Classes

    You may be surprised that _FRAMEWK.VCX contains only two required classes (the application and mediator objects), and in fact even when you add the other subsidiary classes, _FRAMEWK.VCX doesn’t contain much of the functionality you may expect in a Visual FoxPro application. You will not find code to perform table handling. You won’t find dialog boxes filling standard Visual FoxPro roles, such as a dialog box to select report destinations. You won’t find extensive error-handling code.

    _FRAMEWK.VCX doesn’t include this functionality because there is nothing framework-specific about these requirements. Instead, it makes use of several Visual FoxPro Foundation Classes libraries, useful to any framework or application, to perform these generic functions. The _Application superclass contains several members descending from FFC classes, and it instantiates objects from other FFC classes at run time as necessary. Then it wraps these objects, setting some of their properties and adding some specific code and behavior to make these instances of the FFC classes especially useful to the framework.

    For example, _Application relies on its cusError member, descended from the _Error object in FFC\_APP.VCX, to do most of its error handling, and to create an error log. However, as mentioned earlier, _Application code displays the error log using a framework-specific dialog box. The application object also sets the name and location of the error log table to match its own needs, rather than accepting _Error‘s default.

    The framework uses four FFC class libraries: _APP.VCX_TABLE.VCX_UI.VCX, and _REPORTS.VCX. Figure 3 shows these libraries in Class Browser views, as well as in a Classes tab for a framework application project.

    Figure 3. A framework application uses generic Visual FoxPro Foundation Classes, from HOME( )+ FFC folder, to supplement the framework-specific classes in _FRAMEWK.VCX.

    Unlike the subsidiary classes in _FRAMEWK.VCX, the FFC classes and their complex attributes are used directly by _Application, so you don’t specify alternative classes or class libraries for these objects. You can still specify your own copies of these class libraries, as you’ll see in the next section.

    If you examine the Project tab in Figure 3, or the project for any framework application, you’ll find this list of libraries built in. You’ll see _FRAMEWK.VCX, and there will be at least one class library containing the subclasses of _FRAMEWK.VCX for this application.

    You’ll see one more FFC library: _BASE.VCX, which contains the classes on which _FRAMEWK.VCX and all the FFC libraries are based. Your framework project must have access to a library called _BASE, containing all the classes found in _BASE. However, neither the framework nor the four FFC class libraries it uses require any specific behavior or attributes from these classes. You are free to create an entirely different _BASE.VCX with classes of the same name, perhaps descending from your team’s standard base library.

    Framework Templates

    The framework templates are of three types:

    1. Menu templates, a collection of Visual FoxPro menu definition files (.mnx and .mnt extensions)
    2. Metatable, an empty copy of the table the framework uses to store information about the documents (forms, reports, and labels) you use in your application
    3. Text, a collection of ASCII supporting files

    Unlike the .vcx files used by the framework, Visual FoxPro doesn’t deliver separate versions of these templates on disk. Because the templates are copied, rather than subclassed, for framework applications, the templates don’t need to be available to your project as separate files. Instead, these items are packed into a table, _FRAMEWK.DBF, found in the HOME( )+ Wizards folder. The Application Wizard unpacks the files when it generates your new application (see Figure 4).

    Figure 4. The Application Wizard copies template files from this _FRAMEWK.DBF table in HOME( )+ Wizards folder.

    Because the files don’t exist on disk, their template file names are largely irrelevant, except to the Application Wizard. Although we’ll use the template names here, keep in mind that their copies receive new names when the Wizard generates your application.

    Just as the framework identifies “dialog box roles” and supplies sample dialog boxes to fill those roles, it identifies some “menu roles,” and comes equipped with standard menus to meet these requirements. The roles are startup (the main menu for your application) and navigation (a context menu for those forms you identify as needing navigation on the menu).

    There are three template startup menus, each corresponding to one of the three application types described by the Application Builder as normaltop form, and moduleT_MAIN.MNX, is a standard “replace-style” Visual FoxPro menu. It’s used for normal-style applications, which take over the Visual FoxPro environment and replace _MSYSMENU with their own menu. T_TOP.MNX, for top form applications, looks identical to T_MAIN.MNX, but has some code changes important to a menu in an MDI frame. T_APPEND.MNX is an “append-style” menu, characteristic of modules, which are applications that add to the current environment rather than controlling it.

    There is one navigation menu template, T_GO.MNX. Its options correspond to the options available on the standard navigation toolbar (_NavToolbar in _FRAMEWK.VCX).

    Note   Because both T_GO.MNX and T_APPEND.MNX are “append-style” menus, they can exist as part of either _MSYSMENU or your top form menu. The Application Builder synchronizes your copy of T_GO.MNX to work with your normal- or topform-type application. However, if you change your application type manually rather than through the Application Builder, or if you want a module-type application that adds to an application in a top form, you may need to tell these menus which environment will hold them.

    You make this change in the General Options dialog box of the Menu Designer (select or clear the Top-Level Form check box). If you prefer, you can adjust the ObjType of the first record in the MNX programmatically, as the Application Builder does. See the UpdateMenu( ) method in HOME( )+ Wizards\APPBLDR.SCX for details.

    Like the document and toolbar classes in _FRAMEWK.VCX, the menu templates are not required. They simply provide good examples, and should give you a good start on learning how to use menus in a framework application.

    In particular, you’ll notice that the menus do not call procedural code directly, only application object methods. This practice ensures that the code is properly scoped, regardless of whether the MPR is built into an app, or whether the .app or .exe holding the MPR is still in scope when the menu option runs.

    Because Visual FoxPro menus are not object-oriented, they can’t easily hold a reference to the application object. To invoke application object methods, the menus use the object’s global public reference. This reference is #DEFINEd as APP_GLOBAL, in an application-specific header file, like this:

    #DEFINE APP_GLOBAL              goApp
    

    Here is an example menu command using the #DEFINEd constant (the Close option on the File menu):

    IIF(APP_GLOBAL.QueryDataSessionUnload( ),
      APP_GLOBAL.ReleaseForm( ),.T.)
    

    Each template menu header #INCLUDEs this header file. You can change the #DEFINE and recompile, and your menus will recognize the new application reference.

    Note   The application object can manage this public reference on its own (you don’t need to declare or release it). It knows which variable name to use by consulting its cReference property, which holds this name as a string. You can either assign the value in the program that instantiates your application object (as shown in the default main program) or you can assign this string to the cReference property of your _Application subclass at design time.

    The template menus are the only part of the framework using this global reference. If you wish, your forms and other objects can use the reference, too, but there are rarely good reasons to do this. Before you opt to use the global reference, think about ways you might pass and store a reference to the application object in your forms instead. If your forms have mediator objects, they have a built-in method to receive this reference any time they need it.

    Metatable Template

    _FRAMEWK.DBF contains records for T_META.DBF/FPT/CDX, the table holding information about documents for your application. Records in this table indicate whether a document should be treated as a “form” or “report”—and you can create other document types on your own.

    The document type designation is used by the framework dialog boxes descending from _DocumentPicker, to determine which documents are displayed to the user at run time. For example, the _ReportPicker dialog box will not display documents of “form” type, but the _FavoritePicker dialog box displays both forms and reports.

    However, document type as specified in the metatable does not dictate file type. A “report” type document might be a PRG, which called a query dialog box and then ran a report based on the results.

    The Application Builder creates and edits metatable records when you use the Builder to add forms and documents to the application. If you manually add a form or document to a framework project, the Project Hook object invokes the Builder to ask you for details about this document and fill out the metatable accordingly. Of course, you can also add records to the metatable manually.

    The Application Builder and the _FRAMEWK.VCX dialog boxes descending from _DocumentPicker rely on the default structure of this metatable. (You’ll find its structure detailed in**Appendix 2.) The dialog boxes derive from this table the information they need to invoke each type of document, including the options you’ve set in the Application Builder for each document. (Appendix 3 gives you a full list of _DocumentPicker subclasses and their assigned roles.)

    Just as you don’t have to use the _DocumentPicker dialog boxes, you don’t have to use the default metatable structure in a framework application. If you like the idea of the table, you could design a different structure and use it with dialog boxes with different logic to call the _Application methods that start forms and reports.

    Note   If you design a metatable with a different structure from the default, the application object can still take care of it for you. On startup, the metatable is validated for availability and appropriate structure. Once the metatable is validated, the application object holds the metatable name and location so this information is available to your application elements later, even though the application object makes no use of the metatable directly.

    Edit your _Application subclass’s ValidateMetatable( ) method to reflect your metatable structure if it differs from the default. No other changes to the standard _Application behavior should be necessary to accommodate your metatable strategy.

    You can also dispense entirely with a metatable in a framework application. No part of the framework, except the _DocumentPicker dialog boxes, expects the metatable to be present.

    For instance, you might have no need for the dialog boxes or data-driven document access in a simple application. In this case, you can eliminate the metatable and invoke all your reports and forms directly from menu options. Simply provide method calls such as APP_GLOBAL.DoForm( ) and APP_GLOBAL.DoReport( ) as menu bar options. Fill out the arguments in these methods directly in the command code for each menu option, according to the requirements of each form and report.

    Additional Text Templates

    _FRAMEWK.DBF holds copies of some additional text files copied for your application’s use.

    T_START.PRG is the template for the program that initializes your application object and shows the splash screen. Its behavior is well documented in comments you’ll find in the application-specific header file, described later. In addition, as just mentioned, it is not necessary. The program that creates your application object does not have to be the main program for your application, nor does it have to do any of the things that T_START.PRG does.

    For example, suppose your application is a “module type,” handling a particular type of chore for a larger application. Because it is a module, it does not issue a READ EVENTS line or disturb your larger application’s environment. It may or may not need to use the framework’s user log on capabilities; you may have set up a user logging system in the outer program. The outer application may be a framework application, or it may not. All these things will help you decide what kind of startup code you need for this application object.

    Let’s look at some sample code you might want to use for an accounting application. This .exe file is not a framework application, but it has a framework module added to it, which performs supervisor-level actions. Only some users are allowed to have access to this module. When your accounting application starts up, it may have an application manager object of its own, which performs its own login procedures. The method that decides whether to instantiate the framework module might look like this:

    IF THIS.UserIsSupervisor( )
       THIS.oSupervisorModule = ;
          NEWOBJECT(THIS.cMyFrameworkModuleSupervisorClass,;
                    THIS.cMySupervisorAppClassLib)
       IF VARTYPE(THIS.oSupervisorModule) = "O"
          * success
       ELSE
          * failure
       ENDIF
    ELSE
       IF VARTYPE(THIS.oSupervisorModule) = "O"
          * previous user was a supervisor
          THIS.oSupervisorModule.Release()
       ENDIF
    ENDIF
    

    This code does not handle the public reference variable, a splash screen, or any of the other items in T_START.PRG.

    You may not need the public reference variable at all because, in this example, your framework application is securely scoped to your larger application manager object. However, if your module application has menus that use the global reference to invoke your application object, you might assign the correct variable name to THIS.oSupervisorModule.cReference just above the first ELSE statement in the preceding sample code (where you see the “* success” comment). This is the strategy you see in T_START.PRG.

    Note   If many different outer applications will use this module, you will prefer to assign the appropriate cReference string in the class, rather than in this method (so you only need to do it once). You can assign this value to cReference either in the Properties window or in code during startup procedures for the application object. Either way, an assign method on the cReference property in _Application does the rest.

    T_META.H is the template name for the application-specific header file, just mentioned in the section on menu templates. Only the menus and T_START.PRG use this header file, so it is up to you whether you use it, and how you use it. In the preceding example, you might not use it at all, or you might use only its APP_GLOBAL define to set the application object’s global reference.

    The framework uses a few more text templates:

    • T_CONFIG.FPWNot surprisingly, provides a template for the config.fpw generated for your application. The template version gives new Visual FoxPro developers some ideas about what the config.fpw is for (it’s mostly comments); you will almost certainly wish to edit this file to meet your own standards.
    • T_LOG.TXTProvides a startup file for the “action log” the Project Hook will write during the life of your application to let you know what changes it has made to your application while you worked with the project.
    • T_HEAD.TXTProvides a standard header that the Application Wizard uses when generating your application-specific copies of framework templates. You might want to revise T_HEAD.TXT to include your own copyright notices, especially after you’ve edited the rest of the templates.

    Specifying Your Own Framework Components

    If you’ve done any development at all, you’ve undoubtedly experienced moments in which you identify something you wish to abstract from the process of developing a single application. You’ve done it too many times, you know how to do it, and now it’s time you figure out the best way to do it—so you never have to do it again.

    In OOP terms, this is the time to develop a superclass to handle this function, so you can reuse its features. In template terms, this is the time to edit the template you copy for each application’s use. In the Visual FoxPro 6.0 application framework’s mixed environment, as you know, we have both types of components.

    We’ll quickly review how these components are managed automatically by the Application Wizard and Builder during your development cycle. Then we’ll turn our attention to how you integrate your own superclasses and edited templates into this system.

    Framework Components During Your Application Lifecycle

    When you choose to create a new framework application, the Application Wizard takes your choices for a location and project name and generates a project file. If you select the Create project directory structure check box, the Application Wizard also creates a directory tree under the project directory. It adds _FRAMEWK.VCX and the required foundation class libraries to this project. It also adds a class library with appropriate application-specific subclasses of _FRAMEWK.VCX.

    The Application Wizard then adds template-generated, application-specific versions of all the non-OOP components the application needs. As you probably realize, the Application Wizard copies these files out of the memo fields in _FRAMEWK.DBF.

    _FRAMEWK.DBF contains two more records we haven’t mentioned yet: T_META.VCX and T_META.VCT. These records hold straight subclasses of the classes in _FRAMEWK.VCX, and they are copied out to disk to provide your application-specific class library.

    Note   T_META.VCX is not a template. It is just a convenient way for the Application Wizard to hold these subclasses, and is not part of your classes’ inheritance tree. Your subclasses descend directly from _FRAMEWK.VCX when the Application Wizard creates them, and thereafter will inherit directly from _FRAMEWK.VCX.

    Once your new framework project exists, the Application Wizard builds it for the first time. It also associates this project with a special Project Hook object, designed to invoke the Application Builder. The Application Wizard shows you the new project and invokes the Application Builder.

    At this point, the Application Builder takes over. The Application Builder provides an interface you can use to customize the framework aspects of any framework-enabled project, throughout the life of the project.

    You can use the Application Builder to customize various cosmetic features of the application object, such as its icon. When you make these choices, the Application Builder stores them in the appropriate properties of your _Application subclass. (In some cases, it also stores them in the matching _Splash subclass properties.)

    In addition, the Application Builder gives you a chance to identify data sources, forms, and reports you’d like to associate with this project. It gives you convenient access to the data, form, and report wizards as you work, in case you want to generate new data structures and documents. For inexperienced developers, the Application Builder provides a visual way to associate data structures directly with forms and reports, by providing options to invoke report and form wizards each time you add a new data source.

    Whether you choose to generate reports and forms using the wizards or to create your own, the Application Builder and its associated Project Hook object help you make decisions about framework-specific use of these documents. (Should a report show up in the Report Picker dialog box, or is it only for internal use? Should a form have a navigation toolbar?) It stores these decisions in your framework metatable.

    As you think about these automated elements of a framework development cycle, you’ll see a clear difference between the changes you can effect if you change the Application Wizard, or generation process, and the changes you can effect by editing the Application Builder and Project Hook. The files provided by the Wizard, in advance of development, represent your standard method of development. The changes made thereafter, through the Builder and Project Hook, represent customization you can do for this single application.

    The balance of this article concentrates on enhancing the Wizard to provide the appropriate framework components when you begin a new application. Once you have established how you want to enhance the startup components, you will think of many ways you can change the Builder and the Project Hook, to take advantage of your components’ special features, during the rest of the development cycle.

    Note   An important change in versions after Visual FoxPro 6.0 makes it easy for you to customize the Application Builder to match your style of framework use. Rather than directly invoking the default appbldr.scx, the default Application Builder in later versions is a PRG.

    The PRG makes some critical evaluations before it displays a Builder interface. For example, it checks to see whether the project has an associated Project Hook object, and whether this Project Hook object specifies a builder in its cBuilder property. See HOME( )+ Wizards\APPBLDR.PRG for details. You will find it easy to adopt this strategy, or to edit appbldr.prg to meet your own needs for displaying the Builder interface of your choice.

    A preview version of appbldr.prg is included with the source for this article. See appbldr.txt for instructions on making this new Application Builder available automatically from the VFP interface, similar to the new wizard components delivered as part of the document.

    A Closer Look at the Standard Application Wizard

    You’ll find the Visual FoxPro 6.0 Application Wizard files in your HOME( )+ Wizards folder. When you invoke the Application Wizard from the Tools menu, it calls appwiz.prg, which in turn invokes the dialog box in Figure 5, provided by appwiz.scx.

    Figure 5. The standard Visual FoxPro 6.0 Application Wizard dialog box provided by appwiz.scx

    When you choose a project name and location, appwiz.prg invokes HOME( )+ Wizards\WZAPP.APP, the Visual FoxPro 5.0 Application Wizard, with some special parameters.

    The older wizard contained in wzapp.app does most of the work of creating your new project files. The Visual FoxPro 5.0 Application Wizard determines that you are in a special automated mode from the object reference it receives as one parameter and does not show its original interface. It evaluates a set of preferences received from this object reference, and proceeds with the generation process.

    The standard implementation has a number of constraints:

    • Your application subclasses descend directly from _FRAMEWK.VCX. This prevents your adding superclass levels with your own enhancements to the framework, and you certainly can’t specify different superclasses when you generate different “styles” of applications.
    • Your copies of the ancestor classes, in _FRAMEWK.VCX and FFC libraries, are presumed to be in the HOME( )+ Wizards and HOME( )+ FFC directories. Because these ancestor classes are built into your framework applications, and therefore require recompilation during a build, you have to give all team members write privileges to these locations or they can’t use the Application Wizard to start new framework applications. In addition, the fixed locations hamper version control; you may wish to retain versions of ancestor classes specific to older framework applications, even when Microsoft delivers new FFC and Wizards folders.
    • Your non-OOP components are always generated out of HOME( )+ Wizards\_FRAMEWK.DBF. The templates are not easily accessible for editing. The assumed location of _FRAMEWK.DBF prevents you from using different customized template versions for different types of apps, and also presents the same location problems (write privileges and versioning) that affect your use of the framework class libraries. As with your application subclasses, you can’t designate different templates when you generate different types of applications.
    • You have no opportunity to assign a custom Project Hook to the project.

    To allow you to design and deploy customized framework components, a revised Application Wizard should, at minimum, address these points.

    You can make the required changes without major adjustment of the current Application Wizard code, but some additional architectural work provides more room for other enhancements later.

    A New Application Wizard

    If you DO NEWAPPWIZ.PRG, provided in the source code for this article, you will get a dialog box almost identical to Figure 5, and functionally equivalent to the original dialog box. The only difference you’ll notice is a request, on startup, asking you if you wish to register this wizard in your HOME( )+ Wizards\WIZARD.DBF table for future use (see Figure 6).

    Figure 6. The Newappwiz.prg wizard classes can be registered to HOME( )+ Wizards\WIZARD.DBF so you can choose them from the Tools Wizards menu later.

    Though your newly instantiated wizard class calls the old Visual FoxPro 5.0 Wizard code just as the original one did, its internal construction allows completely new generation code to replace this approach in a future version.

    You can call newappwiz.prg with a great deal of information packed into its second parameter, to indicate what wizard class should instantiate and what this wizard class should do once instantiated.

    Why the second parameter, rather than the first? Newappwiz.prg, like appwiz.prg, is designed with the standard wizard.app in mind. wizard.app, the application invoked by the Tools Wizards menu option for all wizard types, uses its registration table, HOME( )+ Wizards\WIZARD.DBF to find the appropriate wizard program to run. Wizard.app passes other information in its first parameter to the wizard program (in this case, newappwiz.prg). Wizard.app passes the contents of the Parms field of wizard.dbf, as the second parameter.

    If you choose Yes in the dialog box in Figure 6, the NewAppWizBaseBehavior class becomes a new choice in the registration table, and fills out its options in the Parms field. Additional NewAppWizBaseBehavior subclasses will do the same thing, registering their own subclasses as separate entries. Once a class is registered in wizard.dbf, you don’t have to call newappwiz.prg directly again.

    If you’ve chosen Yes in the dialog box in Figure 6 and also choose to register the wizard subclass we investigate in the next section, when you next choose the Application Wizard from the Tools menu, you’ll get a choice, as you can see in Figure 7.

    Figure 7. Select your Application Wizard du jour from the Tools Wizards option—once you have more than a single Application Wizard listed in your HOME( )+ Wizards\WIZARD.DBF table.

    An Extended Subclass of the New Wizard: AppWizReinherit

    With an enhanced architecture in place, we can address the issues of component-generation we’ve raised.

    Run newappwiz.prg again, this time with a second parameter indicating a different wizard subclass to instantiate:

      
    

    You should get another message box, similar to Figure 6, asking you if you want to register this subclass in the wizard.dbf table. When you’ve dismissed the message box, you see the dialog box in Figure 8.

    Figure 8. Re-inheritance Application Wizard, page 1

    The first page of this dialog box contains exactly the same options as the standard Application Wizard.

    Note   You’ll find all the visual classes used in the new wizards in newappwiz.vcx, as part of the source code for this article. The container you see on this page of the AppWizFormReinherit class is the same container class used in AppWizFormStandard. You can read more about these dialog box classes in Appendix 4.

    Each subsequent page of the dialog box addresses one of our concerns with the way the original Application Wizard delivers components, and includes some information about how it works. (Figure 9 shows you pages 2 and 3.) Each option defaults to the same behavior you’d get from the original Application Wizard—you don’t need to fill out information on all pages.

    Figure 9. Pages 2 and 3 of the Re-inherit App Wizard provide a layer of superclasses and the locations of your FFC and _FRAMEWK.VCX libraries for this framework application.

    If you change the parent VCX as suggested on the second page of the dialog box, you can have one or more layers of superclasses between your application’s subclasses of _FRAMEWK.VCX. You’ll create team-specific enhancements in these layers.

    Note   This version of the Application Wizard will create the initial classes for you, as subclasses of the components in _FRAMEWK.VCX, if you specify a VCX name that does not exist. Later, you can create more layers of subclasses from the one the Application Wizard derived from _FRAMEWK.VCX, and designate your subclass layer in this dialog box as appropriate. The VCX you designate on the second page of this dialog box should always conform to the following rules:

    • Be the immediate superclasses (parent classes) of the application-specific VCX for this application.–and–
    • Include all the required subclasses of _FRAMEWK.VCX, with the same names as the _FRAMEWK ancestor classes.

    You may want several different branches of your team-specific class levels, to match different types of framework applications you commonly create. For example, you could have one superclass set with your team’s options for a framework module and another one with your team’s topform custom attributes (including the class and classlibrary for your subclass of _topform to provide the correct frame).

    Note   These branches, or types, are not restricted to the “styles” or options you see represented in the Application Builder. They are just part of the normal process of subclassing and enhancing a class tree.

    For example, you may decide to create Active Documents as framework applications. To do so, you’ll need an _Application subclass that is aware of its hosted environment, and makes certain interface decisions accordingly. You’ll also need an ActiveDoc subclass that is aware of the framework’s capabilities and calls application object methods in response to browser-triggered events, just as the menu templates invoke framework behavior.

    Now that you can insert class levels between _FRAMEWK.VCX and your application-specific level, you can make the implementation of these features standard across applications.

    If you change the locations of the FFC and _FRAMEWK.VCX libraries on the “Ancestors” page, the Application Wizard will place appropriate copies of the required class libraries in your specified locations if they don’t exist. The Application Wizard also ensures that your copy of _FRAMEWK.VCX inherits from the proper version of FFC, and that your parent classes point to the proper version of _FRAMEWK.VCX.

    Note   As mentioned in the section “FoxPro Foundation Generic Classes,” your FFC location can include your own version of _BASE.VCX. Your _BASE.VCX does not have to have the same code or custom properties as the original _BASE.VCX, but like your parent classes, your _BASE must include classes descended from the same Visual FoxPro internal classes, with the same names, as the classes in the original _BASE.

    Other FFC libraries, not used in the framework and not described in this article, will not necessarily work with your own _BASE.VCX. For example, if your application uses _GENHTML, the _HTML.VCX library relies on code in the HOME( ) + FFC\_BASE.VCX library. If you use other FFC libraries in your framework application, you may have two _BASE.VCXs included in your project—this is perfectly normal.

    The Application Wizard then focuses on your template files on the next page of the dialog box. If you set a location for your template files, the Application Wizard will create fresh copies of these files (by copying them from the original _FRAMEWK.DBF), ready for you to edit.

    In each case, if the files are already in the locations you supply, the Application Wizard will use the ones you have.

    The last page of the dialog box allows you to pick a Project Hook. The original AppHook class in HOME( ) + Wizards\APPHOOK.VCX is the required ancestor class for a Project Hook designed to work with this application framework, but you can add a lot of team-specific features to your Project Hook subclass. The Application Wizard attempts to verify that the class you specify on this page descends from the appropriate AppHook class.

    When you generate your application, the Application Wizard will create a new set of straight subclasses from your parent VCX (or _FRAMEWK.VCX, if you haven’t changed the default on the “Parents” page). These subclasses become the new T_META.VCX/VCT records in _FRAMEWK.DBF. The Wizard appends new contents for all the other template records of _FRAMEWK.DBF from the template folder, if you’ve named one.

    Note   The first time you and the Application Wizard perform these tasks, it won’t make much difference to the final results. Once the Wizard gives you editable superclass layers and your own copies of the templates, however, you have all the architecture necessary to customize the framework for subsequent uses of the Application Wizard.

    Having replaced _FRAMEWK.DBF records, the Application Wizard proceeds to create your new application much as before, inserting information about your designated Project Hook class at the appropriate time.

    All the “enhanced” Wizard actions are tuned to respect the current setting of the lDelegateToOriginalAppWizard switch, which indicates whether the Visual FoxPro 5.0 Application Wizard code is running or if new code is creating the project. For example, because the original code only looks in the HOME( )+ Wizards folder for _FRAMEWK.DBF, if you have indicated a different place for your _FRAMEWK.DBF (on the “Templates” page) this table will be copied to HOME( )+Wizards before wzapp.app runs. (The first time this occurs, the new Wizard copies your original _FRAMEWK.DBF to a backup file in the HOME( ) + Wizards folder.) Presumably, newer code simply uses your templates table wherever you’ve placed it.

    When you use this Wizard to generate a framework application it saves information about your preferred parent classes, as well as the locations of your FFC and _FRAMEWK libraries and template files, to special _FRAMEWK.DBF records. You won’t need to enter this information, unless you wish to change it. This release of the Application Wizard doesn’t save information about the custom Project Hook subclass you may have specified. However, the next section will show you how to put this information into the Parms of wizard.dbf for default use.

    Note   Because the Application Wizard reads its stored information out of _FRAMEWK.DBF, it can’t get the location of _FRAMEWK.DBF from a stored record! However, you can put this information into the Parms field of wizard.dbf, as described in the next section, so all your developers use the proper version of _FRAMEWK.DBF without having to look for it.

    You may even decide to use a version of this Wizard class, or of its associated dialog box, that only allows some developers to change the “advanced” pages. Other team members can fill out standard information on Page 1, but they’ll still get your improved versions of all the framework components.

    Registering Additional Wizard Subclasses and Customized Records

    The new Application Wizard provides the opportunity to register each subclass of its superclass separately in the wizard.dbf table. The wizard stores its class name and location in the Parms field of its own wizard.dbf record.

    However, you can add more information in the Parms field. You can even store multiple entries in the wizard.dbf for a single subclass, with differently tuned Parms values. The Application Wizard, once instantiated, uses this additional information.

    Here’s the full list of nine options you can pass in the second parameter, or place in the Parms field, for use by NewAppWizBaseBehavior and its subclasses. All #DEFINEs mentioned in this list are in the newappwiz.h header file associated with newappwiz.prg:

    These three options instantiate the Wizard:

    • Wizard classMust descend from #DEFINEd APPWIZSUPERCLASS, defaults to NEWAPPWIZSUPERCLASS.
    • Wizard classlibLibrary containing wizard class, defaults to NEWAPPWIZ.PRG.
    • .App or .exe file nameOptional file, containing the wizard class library.

    These six options are used by the Application Wizard after it instantiates:

    • Wizard form classMust descend from #DEFINEd APPWIZFORMSUPERCLASS, defaults to #DEFINEd NEWAPPWIZFORMSTANDARD.
    • Wizard form classlibLibrary containing the form class, defaults to NEWAPPWIZ.VCX.
    • .App or .exe file nameOptional file containing the wizard form class library.
    • Project Hook classThe Project Hook class you want to associate with this project, if you don’t want to use the default Project Hook class associated with framework-enabled projects. This class should descend from the AppHook class in HOME( )+ “Wizards\APPHOOK.VXC”, so it includes the default functionality, but can include enhancements required by your team.
    • Project Hook classlibThe class library containing the Project Hook class you choose to associate with this project.
    • Template DBFHolding application components, defaults to HOME( )+ Wizards\_FRAMEWK.DBF (#DEFINED as APPWIZTEMPLATETABLE).

    Store these values delimited by commas or carriage returns in the Parms field of wizard.dbf. Similarly, if you call newappwiz.prg directly, you can pass all this information as the program’s second parameter, as a single string delimited with commas or carriage returns.

    After you’ve registered the AppWizReinherit class, the Parms field for this class’ record in wizard.dbf contains the following information:

    APPWIZREINHERIT,<fullpath>\newappwiz.fxp,,AppWizFormReinherit, <fullpath>\NEWAPPWIZ.VCX,,APPHOOK, <fullpath of HOME()+ "Wizards"> \APPHOOK.VCX, <fullpath of HOME()+ "Wizards"> _framewk.DBF
    

    You could run the NEWAPPWIZ program, passing the same string as its second parameter, to get AppWizReinherit‘s default behavior.

    Using our ActiveDoc example just shown, you could create a wizard.dbf entry that invokes the same Wizard class but defaults to a different parent VCX and different menu templates than the rest of your framework applications.

    To accomplish this, you’d edit the information in the ninth value for this row of the wizard.dbf table, which indicates Template DBF, by editing the Parms field.

    Your new row in the table contains the same string in the Parms field, except for the section following the last comma, which points to a new template table. Your special ActiveDoc copy of _FRAMEWK.DBF holds your special Active Document menu templates and superclass information.

    Next, suppose you decide that your ActiveDocument framework applications need a special Project Hook subclass, not just special superclasses and menu templates. You could specify this hook automatically, in the seventh and eighth sections of the Parms field. You might even subclass the AppWizFormReinherit dialog box, to disable the last page of this dialog box for ActiveDocument-type applications, by changing the fourth and fifth sections of the Parms field. (This way, your team members would always use the right Project Hook class when generating this type of framework application.)

    If you made all these changes, this new entry in the wizard.dbf table might have a Parms field that looked like this:

    APPWIZREINHERIT,<fullpath>\newappwiz.fxp,,MyAppWizActiveDocumentDialog, <fullpath>\MyAppWizDialogs.VCX,,MyActiveDocumentAppHookClass, <fullpath> \MyHooks.VCX, <fullpath>\MyTemplates.DBF

    You would also edit the Name field in wizard.dbf for this entry, perhaps to something like “Active Document Framework Application,” to distinguish this entry from your standard values for the AppWizReinherit class.

    When one of your team members accessed the Tools Wizards option from the system menu, “Active Document Framework Application” would now appear on the list of available Wizards, as part of the list you saw in Figure 7. The developer could automatically create the right type of framework application, without making any special choices.

    A Few Parting Thoughts about Team Practices

    You’ll notice a check box in the Reinheritance Wizard‘s dialog box, indicating that you can omit message boxes and generate your new application with no warning dialog boxes or user interaction. Although this is a helpful option once you’ve used this Wizard a few times, please be sure to read all the message boxes, and the information in the edit boxes on the various pages of this dialog box, at least once.

    Any developer’s tool, especially one that edits visual class libraries and other metafiles as extensively as this one does, can potentially cause problems if the system is low on resources. The Help text available within this Wizard attempts to point out its potential trouble spots, so you can close other applications as needed, and have a good idea of what to expect at each step. Other caveats, such as incompletely validated options in this preliminary version, are indicated in the Help text as well.

    You also see a More Info button, which provides an overview of the issues this class is meant to address, and how you can expect it to behave (see Figure 10).

    Figure 10. Wizard documentation under the More Info button

    Beyond its stated purpose to enhance the Application Wizard, AppWizReinherit and its dialog box class try to give you a good model for tool documentation, both at design and run time. The dialog box’s NewAppWiz_Documentation( )GetUserInfo( ), and DisplayDocumentation( ) methods should give you several ideas for implementation of run-time documentation. Newappwiz.prg has a demonstration procedure, BuilderGetDocumentation( ), which shows you how you can apply these ideas to design time documentation for Builders as well. A final demonstration procedure in newappwiz.prg, ReadDocs( ), shows you another aspect of this process.

    Each documentation idea demonstrated here is a variation on a theme: Text is held (using various methods) within the VCX, so it travels with the VCX and will not get lost no matter how widely you distribute the library.

    Whether you use these particular implementations is not important; in many cases you’ll be just as well off if you create a text file with documentation and use Visual FoxPro’s FileToString( ) method to read this information for display by the tool whenever necessary.

    No matter how you decide to implement it, documentation that helps your team better understand the intended use, extension possibilities, and limitations of the tools you build is critical to their adoption and successful use.

    A framework is, in itself, a kind of abstraction, a level above daily activities. Enhancements to a framework represent yet another level of abstraction. Your team will benefit from all the extra attention you can give to communicating your goals for this process.

    With any framework, you can efficiently prototype applications and build complete lightweight applications. With a framework set up the way your team operates, you can accomplish these goals without sacrificing quality, depth, or your normal habits of development. With a framework set to deliver your standard components and practices automatically, even new developers can make meaningful, rewarding contributions to your team effort.

    Appendix 1: The User Option System

    The framework employs a user-registration system based on a user table that is created by the application object if not found at run time. The application object uses the cUserTableName property to set the name and location of this table. If no path is supplied in this property, the location will be set by the cAppFolder property.

    Note    By default, the application object sets cAppFolder to the location of the APP or EXE that instantiated it. If, for some reason, the application object was instantiated outside a compiled APP or EXE container, cAppFolder contains the location of the application object’s VCX.

    If necessary, the application object creates this table in the appropriate location, using the following code (excerpted from the CreateUserTable( ) method):

    lcIDField = THIS.cUserTableIDField
    lcLevelField = THIS.cUserTableLevelField
    * names of two generic-requirement fields,
    * User ID and level, are specified by
    * _Application properties in case you
    * wish to match them to some existing system
    CREATE TABLE   (tcTable) ;
       ((lcIDField) C(60), ;
       (lcLevelField) I, ;
       UserPass  M NOCPTRANS, ;
       UserOpts  M NOCPTRANS, ;
       UserFave  M NOCPTRANS, ;
       UserMacro M NOCPTRANS, ;
       UserNotes M )
    INDEX ON PADR(ALLTR(&lcIDField.),60) TAG ID
    * create a case-sensitive, exact word match
    INDEX ON PADR(UPPER(ALLTR(&lcIDField.)),60) TAG ID_Upper
    * create a case-insensitive, exact word match
    INDEX ON DELETED( ) TAG IfDeleted
    

    If you don’t opt to have users log in and identify themselves in this application, this table is still created. In this case it supplies a default record, representing “all users,” so user macros, favorites, and options can still be stored in this table on an application-wide basis.

    Note   Because of their “global” nature in Visual FoxPro, user macro saving and setting features are only available to framework applications that issue READ EVENTS. Module applications are not allowed to edit the macro set.

    When a user logs in, his password is evaluated using the user table’s UserPass field. A SetUserPermissions( ) method, abstract in the base, is called at this time so the user’s level can be checked in order to make appropriate changes to the application and menu options as well.

    If the login is successful (or when the application starts up assuming no user login for this application), user name and level are stored in the cCurrentUser and iCurrentUserLevel properties.

    User macros, favorites, and options are set from the user’s record in the user table. The _Application code handling macros rely on standard Visual FoxPro abilities to SAVE and RESTORE macros to and from the UserMacro memo field. The favorites system uses an easy-to-read ASCII format in the UserFave memofield. However the options system and the UserOptions field deserve more explanation.

    The user table stores option information in its UserOptions memo field, by SAVEing the contents of a local array. This local array is RESTOREd and copied into a member array, aCurrentUserOpts, to establish user options when the current user is set.

    The array format is fixed, and yet extremely flexible in the types of user options that can be stored. The allowable options include SETs and member properties, and the options should be specified as being “global” to the application or private to a datasession. The array is laid out, to specify these attributes of each option, in four columns, as follows.Expand table

    User Option Array Column 1Column 2Column 3Column 4
    Item nameFor a SET command, the item you’re setting, same as what you’d pass to the SET( ) function.
    For an object, the property you wish to set. Can be the Member.Property you wish to set.
    Value for this itemProperty (.F.)
    or SET (.T.) ?
    Session (.F.)
    or Global (.T.) ?

    Each time a user logs in, the application method ApplyGlobalUserOptions( ) applies SET options and application object property values for all array rows with .T. in the fourth column. The mediator object has the responsibility to call the application method ApplyUserOptionsForSession( ), on your instructions, passing a reference to its parent form. This method applies SET options and form property values for all array rows with .F. in the fourth column.

    The _Options dialog box supplied in _FRAMEWK.VCX gives you examples of all the combinations that can be created for a user option using this array, although its contents are merely examples. It shows you how the user options stored in an array can be expressed as a user interface, giving the user a chance to make changes. It also shows how results of a user-option-setting can be “translated” back into the user options array for use during this login, or saved as defaults to the user preference table.

    You will note that, when the user options to apply changes to the current settings, the Options dialog box reinvokes ApplyGlobalUserOptions( ) and then iterates through the available forms, giving their mediators a chance to reapply session settings if they’re set to do so.

    In many cases, a “global” setting can transferred to forms as well. For example, the _ErrorLogViewer dialog box has a mediator that checks the application’s cTextDisplayFont setting. This is a global user option, because it provides a chance for the user to specify a text font across all the UI of an application. The mediator transfers the value of the cTextDisplayFont to a property of the same name belonging to its parent dialog box. An assign method on this property then applies the fontname value to all members of the dialog box that should reflect the setting.

    Appendix 2: The Default Metatable Structure

    This table shows you the default structure of the framework’s metatable. Appendix 3 shows you how the default _FRAMEWK.VCX dialog boxes use this information.Expand table

    FieldNameTypeUse
    Doc_typeCThis field contains a character to distinguish between document types. Currently, “F” is used for “forms” and “R” is used for “reports.” But this designation just determines how the document type is presented in the interface, not necessarily what type of Visual FoxPro source code file underlies the document. See Alt_Exec and Doc_wrap fields, below.More document types may be added. The framework already contains one extra type, “A,” specifically reserved for you to add application information. The framework will not use “A”-type metatable records in any way, so the reservation of this type simply allows you to use metatable records, or perhaps one metatable header record, as a convenient place for system storage. In most cases, you would want to transfer the contents of such a record to application properties on startup.
    Doc_descrCThe “caption” or long description you want to show up in document picker lists.
    Doc_execMThe name of the file to be run, usually an .scx or .frx file. In the case of a class to be instantiated, this is the .vcx file name.For Form-type documents, the file extension is assumed to be .scx unless this entry is marked “Doc_wrap” (see below) or the Doc_class field is filled out, in which case the extension is assumed to be .vcx.For Report-type documents, the file extension will default to .frx unless this entry is marked “Doc_wrap”. If no .frx file exists by that name, the application object looks for an .lbx file.In all cases, you may also fill out the file extension explicitly.In all cases, if you Include the file to be run in the project, you need not use paths in this field. If you wish to Exclude the file from the project, you may use path information. Assuming your applications install their subsidiary Excluded files to the appropriately located folder, relative pathing should work in the metatable, and is probably the best policy in this case!
    Doc_classMThe class to be instanced, where the Doc_exec is a .vcx file
    Doc_newLMark this .T. for a Form-type document you wish to show up in the FileNew list. When the application object instantiates a form from the FileNew list, it sets its own lAddingNewDocument property to .T. This practice gives the form a chance to choose between loading an existing document or a blank document during the form’s initialization procedures.In many cases, the form delegates this process to its mediator object. The mediator object saves this information for later use.If you do not use a mediator, you may wish to save this information to a form property; you can’t expect the application object’s lAddingNewDocument to reflect the status of any particular form except during the initialization process of that form.For a Report-type document, this field denotes an editable report (new report contents, or even a new report from a template). This capability isn’t currently implemented.
    Doc_openLMark this .T. for a Form-type document you wish to show up in the FileOpen list.For a Report-type document, this field denotes a runnable report or label and will place the item in the report picker list.
    Doc_singleLMark this .T. for a Form-type document that is modeless but should only have one instance. The application object will bring it forward, rather than create a second instance, if the user chooses it a second time.
    Doc_noshowLMark this .T. for a Form-type document that you wish to .Show( ) yourself after additional manipulation, rather than allowing the DoForm( ) method to perform the .Show( ).Note   You will have to manipulate the application’s forms collection or the current _SCREEN.Forms( ) contents to get a reference to this form, so you can manipulate the form and then .Show it when you are ready. If you need this reference immediately, the best place to get it is probably the application object’s aForms[] member array. At this moment, the application object’s last-instantiated form is the one for which you want the reference, and the application object’s nFormCount property has just been refreshed. Therefore, .aForms[THIS.nFormCount] gives you the reference you need when you’re in an application object method (in other code, replace THIS with a reference to the application object). You can see an example of this usage in the _Application‘s DoFormNoShow( ) method.You can create Doc_Wrap programs as described in the entry for the next field. Your wrapper program can take advantage of the DoFormNoShow( ) method, receive its return value (a reference to the form or formset object), and proceed to do whatever you want with it.
    Doc_wrapLIf this field is marked .T. indicating a “wrapped” document, the application’s DoProgram( ) method will run instead of its DoReport( )/DoLabel( ) or DoForm( ) method.If you omit the file extension, the DoProgram( ) method uses the standard Visual FoxPro extension hierarchy to figure out what file you wish to run (“.exe .app .fxp .prg”).
    Doc_goLIf this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard Go context menu for navigation. The menu name is configurable using the application object’s cGoMenuFile property. This field is not used for report-type documents.
    Doc_navLIf this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard navigation toolbar for navigation. The class is configurable using the application object’s cNavToolbarClass and cNavToolbarClassLib properties. This field is not used for report-type documents.
    Alt_execMIf this field is filled out, it takes precedence over the Doc_exec field just described. When the user makes a document choice, the _DocumentPicker’s ExecDocument( ) method converts the contents of this field into a string and runs that string as a macro.Your Alt_exec statement can be anything you choose, and it can use attributes of the metatable, including the Properties field (below) however you want. For example, you can choose to have the metatable editable (on disk) rather than included in the APP/EXE, and you can place information in the Properties field dynamically at run time. Your document would then be able to be “aware” of this information by examining the current contents of the Properties field.
    PropertiesMThis memo field is not used by the framework in any way. It’s for developer use, primarily in conjunction with the Alt_exec field.
    User_notesMThis memo field is not used by the framework in any way. It can be used for notes that would be displayed as Help text for a particular form or report, and so on.

    Appendix 3: Default Document- Management Elements of the Framework

    The framework accesses metatable information through the _DocumentPicker classes. _DocumentPicker is an abstract standard dialog box class, which contains a picklist and a couple of buttons. The working _DocumentPicker subclasses each have their own way of using the information in the metatable to perform two tasks:

    • Show the documents in the picklist.
    • Run the appropriate action when the user picks a document.

    Each subclass stores the relevant metatable fields into an array, which serves as the data source for the list box in the dialog box. The same array holds the metatable information that will eventually act on the user’s choice.

    The _DocumentPicker superclass has an abstract FillDocumentArray( ) method, designed to perform the first service during the dialog box Init( ), and another abstract method called ExecDocument( ), which is triggered whenever/however the user makes a selection from the document list.

    The _DocumentPicker class receives a parameter from the application object. Each subclass of _DocumentPicker uses the parameter to determine which of two states it is supposed to be in when it displays its document list and acts on the user’s choice of a document from the list. The _DocumentPicker superclass simply makes note of this logical value, leaving it to the subclasses to interpret it.

    The various _DocumentPicker’s FillDocumentArray( ) methods concentrate on different document types, and fill the array with the appropriate information for that type. Their ExecDocument( ) methods call different application object methods depending on their document type and the dialog box’s current state, sending information from the metatable from the array to method arguments as needed.

    The first two columns in the table below show you the names of these working classes and the document types that will appear in their lists, courtesy of their FillDocumentArray( ) method. The other columns show the application methods that call them, and the meaning assigned to their two states when ExecDocument( ) is triggered. Each application method listed here takes a logical parameter (defaulting to .F., State 1) to indicate for what purpose the class presents its document list.Expand table

    _DocumentPicker
    Subclass
    _Document typesAssociated _Application methodState 1
    action
    State 2
    action
    _NewOpenformsDoNewOpen( )EditAdd
    _ReportPickerreports and labelsDoReportPicker( )Run report/labelModify/Add not implemented
    in _Application superclass.
    _FavoritePickerdocuments and files of any typeDoStartupForm( )Run document/filePut document / file on Favorites menu for quick access.

    Appendix 4: Using the NEWAPPWIZ Visual Classes

    AppWizFormReinherit, the dialog box called by AppWizReinherit, and AppWizFormStandard, the default dialog box with the same interface as the original wizard, both descend from the same superclass, AppWizFormBaseBehavior (see Figure 11).

    Figure 11. Newappwiz.vcx in the Class Browser

    AppWizFormBaseBehavior is the required superclass for any dialog box provided as the UI of a NewAppWizBaseBehavior or its descendents. The Application Wizard superclass validates your dialog box class when it instantiates the dialog box as descending from this superclass dialog box.

    NewAppWizBaseBehavior contains only the very simple required behavior, no visible controls. It has three custom properties to represent required wizard information (project name, location, and whether or not the Wizard should generate project directory structure). It receives this information from an object reference the Wizard passes. It has a Finish( ) method which passes this information back to the Application Wizard.

    In your subclass of AppWizFormBaseBehavior, you simply databind the interface controls of your choice to these three custom properties. You create other controls and custom properties to represent your enhanced options. Your dialog box calls the Finish( ) method when you’re ready to generate. (Both AppWizFormReinherit and AppWizFormStandard use the OKButton class you see in Figure 11, which contains the call to its parent form’s Finish( ) method.)

    You can augment Finish( ) to pass more options from the dialog box back to your Wizard subclass as necessary.

    You’ll find more information in the NewAppWiz_Documentation method of the superclass. The default AppWizFormStandard subclass shows you a simple example of how to make it work

    Microsoft Transaction Server for Visual FoxPro Developers 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. What Is Microsoft Transaction Server?
    3. Why Is MTS Important for Visual FoxPro Developers?
    4. Creating Your First MTS Server

    Show 12 more

    Randy Brown
    Microsoft Corporation

    October 1998

    Summary: Discusses using Microsoft® Visual FoxPro® version 6.0 with MTS to develop three-tier applications. (36 printed pages).

    Contents

    Introduction What Is Microsoft Transaction Server? Why Is MTS Important for Visual FoxPro Developers? Creating Your First MTS Server Setting Up Security The Basic Features of MTS Just-In-Time Activation Transactions Programming Models Deployment Remote Deployment and Administration Security Shared Property Manager MTS Support for Internet Information Server Automating MTS Administration Tips and TricksExpand table

    Click to copy the sample files associated with this technical article.

    Introduction

    No doubt you’ve heard all about Microsoft Transaction Server (MTS) and how it will make your life easier to develop three-tier applications. This article offers a good primer on using Visual FoxPro 6.0 with MTS. We cover the basics of using MTS and then extend it to using with Visual FoxPro Component Object Model (COM) Components. This document is intended to be used with the Microsoft PowerPoint® slide show included with the Visual FoxPro sample files.

    MTS is a great environment for working with three-tier development. However, one should realize that it is simply not just a matter of dropping your Visual FoxPro servers into an MTS package and expecting miracles. While it is true that much of the work is already done for you, nothing comes for free. Performance and scalability are critical factors that require well-thought-out designs. Good MTS applications are designed with MTS in mind from the start!

    This article assumes that you have MTS already installed. It is available in the Microsoft Windows NT® version 4.0 Option Pack, available from the Microsoft Web site at https://www.microsoft.com/windows/downloads/default.asp.

    In addition, you should familiarize yourself with the basics of MTS. Information is available in the Help files provided with MTS when you install the Windows NT 4.0 Option Pack.

    What Is Microsoft Transaction Server?

    MTS is a component-based transaction processing system for building, deploying, and administering robust Internet and intranet server applications. In addition, MTS allows you to deploy and administer your MTS server applications with a rich graphical tool (MTS Explorer). MTS provides the following features:

    • The MTS run-time environment.
    • The MTS Explorer, a graphical user interface for deploying and managing application components.
    • Application programming interfaces (APIs) and resource dispensers for making applications scalable and robust. Resource dispensers are services that manage nondurable shared state on behalf of the application components within a process.

    The MTS programming model provides a framework for developing components that encapsulate business logic. The MTS run-time environment is a middle-tier platform for running these components. You can use the MTS Explorer to register and manage components executing in the MTS run-time environment.

    The three-tier programming model provides an opportunity for developers and administrators to move beyond the constraints of two-tier client/server applications. You have more flexibility for deploying and managing three-tier applications because:

    • The three-tier model emphasizes a logical architecture for applications, rather than a physical one. Any service may invoke any other service and may reside anywhere.
    • These applications are distributed, which means you can run the right components in the right places, benefiting users and optimizing use of network and computer resources.

    Why Is MTS Important for Visual FoxPro Developers?

    Microsoft is investing a great amount of resources in three-tier development because of a multitude of benefits derived from this architecture. As shown in Figure, Tier 2, the so-called “middle tier,” represents the layer where much of the Application Services/Business Logic is stored. Visual FoxPro COM components are ideally suited for this architecture and will play a key role in this tier for many years to come. This middle tier is also where MTS lives.

    Figure 1. Web-enabled three-tier architecture

    Future applications will consist of Web based front ends using a combination of HTML/XML. While Visual FoxPro data can be used as your database of choice for Tier 3, your applications should be written to communicate to a generic back end. This should be a test of your application’s extensibility. “How easy is it to swap back ends—let’s say Visual FoxPro database to Microsoft SQL Server™?” There are several options, including Open Database Connectivity (ODBC) and ActiveX® Data Objects (ADO), which provide generic interfaces to data. Remember, your application should be written knowing that any or all of the three tiers can be swapped out independent of each other.

    So why is MTS great for Visual FoxPro developers? It should be clear now that the ability to swap out tier components at will makes for a great reusability story. Microsoft has a concept called total cost of ownership (TCO), which means the collective cost of providing and maintaining corporate Information Services. The three-tier model goes a long way toward reducing TCO.

    Updating the Presentation layer is very easy because it merely involves one having to refresh his/her browser. Windows front ends consisting of Visual FoxPro/Visual Basic® forms offer more flexibility in user interface, but updating 150 sites can be time-consuming. In addition, one should expect improved UI options available in HTML.

    The back-end data is usually the tier that changes the least. Having data managed centrally also reduces costs. Remember that data can be distributed and still managed from one location. It doesn’t have to be stored centrally to be managed centrally.

    Finally, we get to Visual FoxPro’s role in the middle tier. Middle-tier components tend to change most often because they represent business rules, which change as the needs of the business changes. Traditional client/server and monolithic applications would often combine the first two layers into one. This was very inefficient because of the distribution costs in updating sites. Today, with browsers, much of this distribution problem goes away. However, business rules are often complex and can contain sensitive/secure information, so it’s not always wise to send these rules back with the HTML to a Web browser. In addition, it can impede performance.

    So, we end up with a dilemma. We want to limit the amount of information sent back to the client, but we also want to minimize the number of back and forth trips between client and server, because bandwidth is also a big consideration (more so with the Internet versus an intranet). The best solution is one involving a so-called “Smart Client.” Traditionally, the Web browser is thought of as an unintelligent client whose job is to merely display an entire static Web page. Each time something on the page changes, we need to refresh the entire Web page. With dynamic HTML (DHTML), you no longer need to do this. Only parts of the Web page affected need updating. In addition, some of the business rules can (and should) reside on the client, thus reducing round trips to the server. For example, you may want to have your client have simple data validation rules, such as one to ensure a value is not negative. It would be more efficient to perform these sorts of checks on the client. Most of the rules, especially sensitive ones, will exist on the server away from client eyes. It is also important to realize, however, that client-side business rules are subject to change almost as frequently as those on the server. The ATSWeb application (available at https://msdn.microsoft.com/vfoxpro/ats_alpha/default.htm) offers a great example of business rules being applied to both client and server.

    MTS provides an environment for hosting your Visual FoxPro middle-tier objects because it handles many of the common tasks, including resource and thread management, security, deployment, application robustness, and transactions. This leaves you, the developer, with only the responsibility of providing business logic specific to your application.

    Creating Your First MTS Server

    Let’s jump right in and create an MTS server, because it’s very simple if you already know how to create a Visual FoxPro COM component.

    Creating a Visual FoxPro COM Component

    1. Create a new project file called test1.pjx
    2. Create a new program file (PRG) called test1.prg
    3. Add the following code to this program:DEFINE CLASS server1 AS custom OLEPUBLIC PROCEDURE hello RETURN “Hello World” ENDPROC ENDDEFINE
    4. Build the server as a DLL (for example, test1.dll). All MTS components must be created as in-process DLL servers. You now have a server that can be tested directly in Visual FoxPro:x=create(“test1.server1”) ? x.hello()

    Adding the Visual FoxPro COM Component to an MTS Package

    A package is a collection of components that run in the same process. Packages define the boundaries for a server process running on a server computer. For example, if you group a Sales component and a Purchasing component in two different packages, these two components will run in separate processes with process isolation. Therefore, if one of the server processes terminates unexpectedly (for instance, because of an application fatal error), the other package can continue to execute in its separate process.

    This section describes the task of installing the Visual FoxPro server into the MTS environment.

    1. Launch MTS Explorer.
    2. In the left pane, navigate to the Computers item and select My Computer. You are now looking at the MTS environment.
    3. Click the Packages Installed node to view all default packages installed by MTS. You can think of a Package as a set of components that perform related application functions. For example, an Inventory package might consist of two DLLs, each performing a task related to checking product inventory for a customer order.
    4. Let’s create a new package now. Select the Action -> New -> Package menu item.
    5. Click the Create an empty package button. Type in a name for your new package (for example, Foxtest1).
    6. Click the Next button, and then click the Finish button. You should now see your new package added under the Packages Installed node.
    7. Click your new package node (for example, Foxtest1). You should now see two items. The Components folder is where you add new components such as the Visual FoxPro component you just created. The Roles folder is where you set up groups of users (roles) who all share similar access privileges (security). You do not need to add anything to the Roles folder in order to use your Visual FoxPro component with MTS.
    8. Click the Components folder and select the Action -> New -> Component menu item.
    9. Click the Install new component(s) button. This will bring up the Install Components dialog box. Click the Add files button and go to the location where you created your Visual FoxPro server (for example, test1.dll). Select both the .dll and .tlb files. The .tlb file is the type library file containing properties and methods of your server. After selecting these two files, you should see your OLEPUBLIC component listed in the lower panel. Click Finish and you should see your server added to this folder.
    10. At this point, your package is complete and ready to go. Later, we will talk about setting Transaction support. This can be done from the Properties dialog box of your server.

    Accessing Your Component

    You can now test your new MTS packaged component using a command similar to the one used to test Visual FoxPro after the DLL server was first created.

    x=create("test1.server1")
    ? x.hello()
    

    That’s all you need to do! If you go back into the MTS Explorer, you should see the component represented with a spinning icon. Click the Status View to see details about the state of the object.

    Figure 2. New component viewed in MTS Explorer

    If you release the object (RELEASE x), MTS releases its reference.

    Going Forward

    We’ve just discussed the basics of installing your Visual FoxPro server in MTS. Essentially, all we did was wrap the Visual FoxPro component inside an MTS process that manages security, transaction state, fault tolerance, and other common server responsibilities. All Visual FoxPro servers used with MTS are registered this way. The remainder of the article discusses how to take advantage of MTS-specific features such as security and transactions. You can write code in your components that talk directly to the MTS run-time environment. In addition, the above process can be entirely automated, because MTS exposes an administrative Automation interface.

    Setting Up Security

    So why are we starting out so early with security? Well, sooner or later, you’re going to fiddle with some sort of security switch and suddenly that MTS application of yours will no longer work. It’s important that you follow these instructions and refer to them later when you decide to add security to your applications.

    Note   MTS 2.0 security setup is described in the Readme document. If you have MTS installed on Microsoft Windows® 95, you can skip this section.

    Setting System Package Identity

    Before you do anything in MTS, it is a good idea to configure the system package for administrating security. When installing MTS, set the system package identity before creating any new packages as follows:

    1. Create a new local Windows NT group named “MTS Administrators” and a new local user named “MTS Administrator.”
    2. Add the “MTS Administrator” user to the “MTS Administrators” and “Administrators” groups.
    3. Set the identity of the system package to “MTS Administrator.” If this does not work, try setting this to the Administrator user.

    Note   You cannot set a package’s identity to a group.

    1. Shut down the system package so that it will be restarted with the new identity. You can do this by right-clicking the My Computer icon in MTS Explorer and selecting Shut Down Server Processes.

    Adding Security for MTS Packages

    You first need to determine whether you want all or just a few components in your Package to have security. Right-click the Package and select Properties. Next, click the Security tab. Then check the Enable authorization checking check box. To enable or disable security at a component level, right-click a component and display the Properties dialog box.

    If this is all you do, an “Access is denied” error message is generated when you try to access your component. You MUST associate a valid role with any component marked for security!

    Right-click the package’s Roles folder and select New Role. Type in a functional role such as Managers, Accountants, and so on.

    The new role is added as a subfolder. Right-click this folder to Add New User (you will get a dialog box to Add Users and Groups to Role). Select the user(s) that you want to add to your role. To finish, select the Role Membership folder under each component that is marked for security and add the new role created in step 3 by right-clicking the folder and selecting New Role.

    Note   You may still experience the “Access is denied” error message when running your components. There are a couple of possible solutions:

    • Sometimes adding a Group to a role does not work (step 3). You might try adding individual users instead.
    • The user rights for that user are not properly set. Make sure the user account for the identities of the system package and other MTS packages have the Windows NT “Log on as a service” user right. You can verify this by using the Windows NT User Manager:
    1. From the Policies menu, select User Rights.
    2. Click Show Advanced User Rights.

    Tips for Visual FoxPro Users

    Much of the security administration can easily be handled by Automation using the MTS Admin objects. You can set up Security administration in the AfterBuild event of a ProjectHook class you have tied to the project that generates your MTS COM DLL server. See the section “Using Visual FoxPro 6.0 Project Hooks” for examples.

    The Basic Features of MTS

    Before we jump right into using Visual FoxPro with MTS, let’s review some basic concepts that you need to know in order to make effective use of the MTS environment. For more detailed information, see MTS Help.

    Activity

    An activity is a collection of MTS objects that has a single distributed thread of logical execution. Each MTS object belongs to a single activity. This is a basic concept that describes how the middle-tier functions when confined to the MTS environment. In an MTS package, multiple clients can access objects, but only one object per client is running at a time on a single thread.

    Context

    Context is state that is implicitly associated with a given MTS object. Context contains information about the object’s execution environment, such as the identity of the object’s creator and, optionally, the transaction encompassing the work of the object. The MTS run-time environment manages a context for each object.

    As a developer, think of every Visual FoxPro object that is registered in an MTS package as having an associated Context object that is created every time you instantiate the Visual FoxPro object. So, each time you issue a CreateObject command, two objects are created—your server and its associated Context. In fact, you can return an object reference to this Context object directly in your code, as in the following example:

    #DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
    LOCAL oMTX,oContext
    oMtx = CREATEOBJECT(MTX_CLASS)
    oContext = oMtx.GetObjectContext()
    

    The Context object has the following properties and methods.Expand table

    CountCreateInstanceDisableCommit
    EnableCommitIsCallerInRoleIsInTransaction
    IsSecurityEnabledItemSecurity
    SetAbortSetComplete 

    As you can see, the properties, events, and methods (PEMs) are used to access information related to the object transaction and security context (see MTS Help for more details on specific syntax for these PEMs). It is important to understand that the Context state is inherited. An object in a package called from another object in the same package will inherit the state of its caller. Because Context is confined within the same process, state, such as security, is trusted. No object in a package needs to explicitly provide its own security. When your object is released, so is its Context.

    Package

    Packages, as we just described, are the building blocks of MTS. Think of them as mini applications—a set of components that perform related application functions. All components in a package run in the same MTS process.

    Remember, “Good MTS applications are designed with MTS in mind from the start.” You should design your Package contents with your entire application in mind. Each package runs in its own process, so try to design packages that don’t attempt to do more than they absolutely need to. There are performance advantages to maintaining many components within in a single package, but there may also be security constraints (roles) that dictate a different architecture.

    Packages are also the primary means of deployment. The MTS environment allows one to export the contents of a Package to a nice distributable setup (both client and server). We’ll discuss this in the “Deployment” section.

    Role

    A role is a symbolic name that defines a class of users for a set of components. Each role defines which users are allowed to invoke interfaces on a component. A role is the primary mechanism to enforce security. Role-based security is handled at the component level. It’s possible that this may be at the method level in a future version of MTS. Security cannot be enforced on the Windows 95 version of MTS.

    Roles are stored at the package level. Each component in a package can belong to one of more of the defined roles. For example, an Inventory package might contain a Visual FoxPro server whose responsibility is to handle inventory. There are two roles defined in this package: Managers and Clerks. These two roles are simply collections of Windows NT users/groups with a collective name that you provide. Your server is coded so that Clerks can access inventory data for normal order entries and reporting. Managers have additional power in that they can override inventory levels to make adjustments (for example, quarterly product shrinkage estimates).

    You can set up security so that it is automatically handled (for instance, users not in roles are given “Access is denied” error message), or you can manage it programmatically through code. The Context object’s IsCallerInRole method is ideal for this.

    Resource Dispensers

    A resource dispenser manages nondurable shared state on behalf of the application components within a process. Resource dispensers are similar to resource managers, but without the guarantee of durability. MTS provides two resource dispensers:

    • The ODBC resource dispenser
    • The Shared Property Manager

    Resources are shared within the same process—same process = same package. In the section “Shared Property Manager,” we discuss programmatically accessing shared properties. This is a really cool thing for Visual FoxPro developers because it allows multiple instances of objects to share state information. For example, you could have a counter that tracks the last ID number used by a database.

    ODBC resource dispenser

    The ODBC resource dispenser manages pools of database connections for MTS components that use the standard ODBC interfaces, allocating connections to objects quickly and efficiently. Connections are automatically enlisted on an object’s transactions and the resource dispenser can automatically reclaim and reuse connections. The ODBC 3.0 Driver Manager is the ODBC resource dispenser; the Driver Manager DLL is installed with MTS.

    Shared Property Manager

    The Shared Property Manager provides synchronized access to application-defined, process-wide properties (variables). For example, you can use it to maintain a Web page hit counter or to maintain the shared state for a multiuser game.

    Resource Managers

    A resource manager is a system service that manages durable data. Server applications use resource managers to maintain the durable state of the application, such as the record of inventory on hand, pending orders, and accounts receivable. Resource managers work in cooperation with the Microsoft Distributed Transaction Coordinator (MS DTC) to guarantee atomicity and isolation to an application. MTS supports resource managers, such as Microsoft SQL Server version 6.5, that implement the OLE Transactions protocol.

    The MS DTC is a system service that coordinates transactions. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers. MS DTC was first released as part of SQL Server 6.5 and is included in MTS, providing a low-level infrastructure for transactions. MS DTC implements a two-phase commit protocol to ensure that the transaction outcome (either commit or abort) is consistent across all resource managers involved in a transaction. MS DTC ensures atomicity, regardless of failures.

    You might be asking if Visual FoxPro is a resource manager, because it has its own native database. Unfortunately, the answer is no. Visual FoxPro transactions are native to Visual FoxPro and do not go through the MS DTC. Therefore, automatic transaction support within MTS is not supported for Visual FoxPro data. You cannot use the Context object’s SetAbort method to abort a transaction if the data is stored in Visual FoxPro databases/tables. The database must either support OLE Transactions (SQL Server) or be XA-compliant (Oracle).

    Base Clients

    A base client is simply a client that runs outside of the MTS run-time environment, but instantiates MTS objects. In a three-tier architecture, a base client is typically the presentation layer, such as an application form or Web page. The base client neither knows nor needs to know that MTS is used in the middle tier. It merely creates an instance of an object that exists in an MTS package and awaits a response. The following table describes some of the differences between a base client and an MTS component, such as a Visual FoxPro DLL server.Expand table

    Base clientMTS component
    Can be EXEs, DLLs.Must be in-process DLL.
    MTS does not manage its process.Manages server processes that host MTS component.s
    MTS does not create or manage threads used by application.Creates and manages threads.
    Does not have implicit Context object.Each MTS object has own Context object.
    Cannot use Resource Dispensers.Can use Resource Dispensers.

    Just-In-Time Activation

    Just-in-Time (JIT) activation is the ability to activate an MTS object only as needed for executing requests from a client. Most Visual FoxPro developers are familiar with object instantiation, as in the following code:

    myObject = CreateObject("myclass")
    myObject.myMethod()
    myObject.myProperty = 123
    RELEASE myObject
    

    A “stateful” object created by this code retains state during the lifetime of the object (until it is released). This means that property values (such as myProperty) are retained between statement execution. When the object is finally released, all object references and state are released.

    There is overhead with creating objects from your Visual FoxPro components. Each time you instantiate an object, Visual FoxPro needs to allocate a certain amount of memory. In addition, the first time you create an object, Visual FoxPro takes a little extra time to load its run-time libraries. When the last instance is released, the entire Visual FoxPro run time is also released.

    JIT activation addresses many of these memory issues that affect performance. The first thing JIT does is cache the server’s run-time libraries in memory, even though no outstanding object references exist. The first time you instantiate a Visual FoxPro server that’s in an MTS package, the Visual FoxPro run time loads the address space of the MTS process. When you release the object, MTS still keeps the libraries in memory for a specified amount of time. You can change this setting in the package’s property sheet (default = 3 minutes). This saves having to reload the run time when the object count hits 0.

    The main thing that JIT activation offers is ability to transform your object from “stateful” to “stateless” mode. In the preceding example, you can interpret a “stateless” object as one having the initial default settings. So, in the example, the value of myProperty would be reset to its original setting. A stateless object is managed by MTS and is very lightweight, so it consumes much less memory. The only thing keeping the stateless object alive is the object reference held onto by the client. Internally, MTS recycles threads consumed by stateful objects when they go stateless. When a method is invoked on that object, it then becomes stateful again on a thread that could be different from the one originally created on.

    Putting your objects into a stateless mode is handled easily by the Context object. The following code illustrates putting an object in a stateless mode:

    #DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
    LOCAL oMTX,oContext
    oMtx = CREATEOBJECT(MTX_CLASS)
    oContext = oMtx.GetObjectContext()
    oContext.SetComplete()
    

    This code is actually called from within a method of your Visual FoxPro server. You can see if your object is stateless by viewing the status of your component in the MTS Explorer. A stateless object appears in the Objects column, but not in the Activated or In Call columns.

    Use the SetComplete method to put the object in a stateless mode. Use SetComplete for committing transactions (as we discuss in the next section, “Transactions”). You can also use SetAbort to make an object stateless.

    Again, when you change an object to stateless, all property settings revert to their original defaults. When you invoke a method (or property set/get) on this stateless object, the object is activated (goes stateful) and the object’s INIT event is fired. When you call SetComplete, the object DESTROY event is fired.

    Note   Any state that exists on the object is lost when the object is deactivated (SetComplete). If you need to save state, you should either persist information to a database or use the MTS Shared Property Manager.

    Because your object’s INIT is called whenever your object goes from Stateless to Stateful, you should try to minimize the amount of code in this event.

    Here is a simple scenario showing interaction between client and MTS server.

    Visual FoxPro server code:

    DEFINE CLASS mts2 AS Custom OLEPUBLIC
       MyColor = "Green"
       PROCEDURE InUsa (tcCustID)
          LOCAL llInUSA,oMTX,oContext
          oMtx = CreateObject("MTXAS.APPSERVER.1")
          oContext = oMtx.GetObjectContext()
          llInUSA = .F.
          USE CUSTOMER AGAIN SHARED
          LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
          IF FOUND()
             llInUSA = (ATC("USA",country)#0)
          ENDIF
          oContext.SetComplete()
          RETURN llInUSA
       ENDPROC
    ENDDEFINE
    

    Base client executes following code:

    LOCAL oCust,cCust,lUsa
    oCust = CreateObject("vfp_mts.mts2")
    ? oCust.MyColor
    Green
    oCust.MyColor = "Red"
    ? oCust.MyColor
    Red
    cCust = "JONES"
    lUsa = oCust.InUsa(cCust)   && object goes stateless (deactivated)
    ? oCust.MyColor      && object is activated (stateful)
    Green
    RELEASE oCust         && object is fully released
    

    Notice in the preceding example how the state of oCust is lost after the InUsa method is called. The MyColor property no longer returns Red, but is instead reset to its original value of Green.

    Transactions

    If you have used Visual FoxPro at all, you are probably aware that Visual FoxPro supports transactions. Changes to your data can be committed or rolled back. Though transactions are critical to MTS, don’t be misled by the name; there is a lot more to it than just transactions. However, the ability to have MTS automatically handle transactions between distributed objects is quite powerful. Transactions are often discussed in terms of the ACID acronym:

    • Atomicity—ensures that either the entire transaction commits or nothing commits.
    • Consistency—a transaction is a correct transformation of the system state.
    • Isolation—protects concurrent transactions from seeing each other’s partial and uncommitted results.
    • Durability—committed updates to managed resources can survive failures.

    As just mentioned, MTS transaction support is not compatible with Visual FoxPro data. It only works with databases supporting OLE transaction or XA protocols. Both SQL Server and Oracle data can be used with MTS in transactional fashion.

    You should understand what we mean by a transaction and to what extent things are either committed or rolled back. Consider the following scenario (all done within confines of two components in a single MTS package):

    1. Component A adds a new customer record to the Customer table in SQL Server.
    2. Component A writes out new record to a Visual FoxPro database (audit log).
    3. Component A sends e-mail notification of new customer to some manager.
    4. Component A calls Component B.
    5. Component B edits the Orders table with a new order in SQL Server.
    6. Component B writes out text log file of activity.
    7. Component B completes activity by committing the transaction (SetComplete).
    8. Component A discovers bad credit history with customer and aborts transaction (SetAbort).

    When Component B commits in step 7, not a whole lot happens because MTS manages the entire Context within the package in a distributed fashion. Component B actually inherits transaction state from Component A, so it cannot really fully commit the transaction. The real transaction terminates in step 8 when the last object with transaction state aborts. At this point, changes made to both Customer and Orders tables are rolled back because these tables are in SQL Server. Unfortunately, the Visual FoxPro table update, e-mail notification, and text log file activities are not rolled back. When a transaction is aborted/committed, only data managed through the MS DTC is affected. There is no event that is magically triggered. (Check out the MTS SDK for ideas on using Spy).

    Remember, good MTS apps are written with MTS in mind from the start. Managing transactions is very important, and while much of it is handled automatically, you will need to provide a fair amount of code to effectively manage all the resources being utilized in a transaction setting.

    Transaction support is set at the component level, but transactions can span multiple packages. You can set this option in the MTS Explorer from the component’s Property Sheet (see MTS Help for details on the various options). Again, the object’s Context manages and passes on transaction state for a given component. If the transaction setting of a component is marked as “Requires a transaction,” a transaction is always associated with the component. If another object that calls this component already has a transaction in effect, no new transaction is created. The component merely inherits the current one. A new transaction is only created if one does not already exist in the context.

    Figure 3. Setting Transaction support

    Let’s return a minute to the SetComplete and SetAbort methods. These methods actually serve two purposes. From their names, they imply functionality related to transactions. However, as already discussed, they also serve to deactivate objects (make them stateless). In fact, these methods can be used simply for JIT activation without any concern for transactional support. Again, SetComplete releases valuable resources/memory used by MTS to allow for improved scalability. The Context object also includes several other methods useful for transactions: EnableCommitDisableCommit, and IsInTransaction. The following example shows how to handle transactions in Visual FoxPro:

    LPARAMETER tcCustID
    LOCAL lFound,oMTX,oContext
    oMtx = CreateObject("MTXAS.APPSERVER.1")
    oContext = oMtx.GetObjectContext()
    USE CUSTOMER AGAIN SHARED
    LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
    lFound = FOUND()
    IF FOUND()
    oContext.SetComplete()
    ELSE
    oContext.SetAbort()
    ENDIF
    RETURN lFound
    

    In this scenario, we assume that another component already performed an update on another table (for example, Orders). If the customer ID in the preceding code was not found, the entire transaction would be rolled back.

    You’re probably wondering how transactions work in the code, which clearly appears to be against Visual FoxPro data. Actually, this example is using Remote Views against SQL Server data. Again, Visual FoxPro tables do not support OLE transactions, so you will not get MTS transaction support if you use DBF tables. However, data updates either to Remote Views or by SQL pass-through work just fine.

    **Tip   **Make sure that your connection to a remote data source is made without any login dialog box. If you are using a connection stored in a DBC, ensure that the Display ODBC logins prompt is set to Never. For access to remote data through SQL pass-through commands, you can use the SQLSetProp function:

     SQLSETPROP(0, 'DispLogin', 3)
    

    Programming Models

    MTS supports two programming models. The TransactionContext model is intended primarily for backward compatibility. It essentially lets the base client control the transaction. The assumption is that the COM component has no MTS awareness (that is, the component was written before MTS was available). The second model is called the ObjectContext model and assumes the COM component inside the MTS package has MTS smarts and is aware of its Context object.

    TransactionContext

    We do not recommend using this model for new three-tier applications, because it has limited access to the full capabilities of MTS. It merely offers a way to provide some transaction support to applications whose middle-tier components were developed without MTS in mind. The burden of transaction handling rests on the base client. With this model, the base client is likely to be a smart client that has scripting capabilities (for example, an application form). The base client is less likely to be a Web page, and it always runs outside of the MTS run-time environment.

    The following code snippet in a Visual FoxPro form (base client) shows this model in use. The middle-tier component is a Visual FoxPro server whose ProgID is “vfp_mts.mts1”. The assumption here is that this server knows nothing about MTS, thus requiring the base client to perform all transaction handling:

    #DEFINE TRANS_CLASS   "TxCtx.TransactionContext"
    THIS.oContext = CreateObject(TRANS_CLASS)
    LOCAL loCust
    loCust = THISFORM.oContext.CreateInstance("vfp_mts.mts1")
    RETURN loCust.lnUSA
    

    The code in the middle tier simply does a lookup in a SQL Server table for a customer’s home country. If the record was actually changed, the base client would have the capability to actually commit or roll back the transaction. The TransactionContext object only supports three methods: CreateInstanceCommit, and Abort.

    ObjectContext

    The ObjectContext model is the only model you should consider for new MTS application development. It relies on component awareness of MTS, but this should be your goal so that you can optimize performance and take advantage of MTS-specific features.

    Unlike the TransactionContext object, which uses the following PROGID:

    #DEFINE TRANS_CLASS   "TxCtx.TransactionContext"
    

    the ObjectContext object can be accessed using the following code:

    #DEFINE MTX_CLASS   "Mtxas.AppServer.1"
    

    The ObjectContext object, which can be referenced in your Visual FoxPro code, as shown here:

    LOCAL oMTX,oContext
    oMtx = CreateObject("MTXAS.APPSERVER.1")
    oContext = oMtx.GetObjectContext()
    

    contains the following properties, events, and methods (PEMs).Expand table

    PEMDescription
    CountReturns the number of Context object properties.
    CreateInstanceInstantiates another MTS object.
    DisableCommitDeclares that the object hasn’t finished its work and that its transactional updates are in an inconsistent state. The object retains its state across method calls, and any attempts to commit the transaction before the object calls EnableCommit or SetComplete will result in the transaction being aborted.
    EnableCommitDeclares that the object’s work isn’t necessarily finished, but its transactional updates are in a consistent state. This method allows the transaction to be committed, but the object retains its state across method calls until it calls SetComplete or SetAbort, or until the transaction is completed.
    IsCallerInRoleIndicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
    IsInTransactionIndicates whether the object is executing within a transaction.
    IsSecurityEnabledIndicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
    ItemReturns a Context object property.
    SecurityReturns a reference to an object’s SecurityProperty object.
    SetAbortDeclares that the object has completed its work and can be deactivated on returning from the currently executing method, but that its transactional updates are in an inconsistent state or that an unrecoverable error occurred. This means that the transaction in which the object was executing must be aborted. If any object executing within a transaction returns to its client after calling SetAbort, the entire transaction is doomed to abort.
    SetCompleteDeclares that the object has completed its work and can be deactivated on returning from the currently executing method. For objects that are executing within the scope of a transaction, it also indicates that the object’s transactional updates can be committed. When an object that is the root of a transaction calls SetComplete, MTS attempts to commit the transaction on return from the current method.

    Deployment

    Microsoft Transaction Server offers excellent tools for deploying both client- and server-side setups. Setups are made at the package level, so you should include all components for your application in a particular package. The deployment package contains all the distributed COM (DCOM) configuration settings you need, so you don’t have to fuss with the messy DCOM Configuration dialog box.

    To create a setup

    1. Click the package that you want to create setup.
    2. Select Export… from the Action menu. The Export dialog box is displayed.

    Figure 4. Exporting a package

    **Important   **The directions in the Export dialog box are not very clear. You should not simply type in a path as specified. If you do, the Export routine creates a file with a .pak extension in the folder location you specify. Instead, you should always type a full path and file name for the .pak file, as shown in Figure 4.

    You can also use the scriptable administration objects to automate deployment and distribution of your MTS packages. See the section “Remote Deployment and Administration” to follow for more details.

    The output of the Export operation consists of two setups:

    Server Setup

    This setup, which is placed in the folder specified in the Export dialog box, contains the .pak file and all COM DLL servers used by the package.

    Note   With Visual FoxPro servers, you will also have .tlb (type library) files included. You can install this package by selecting Install from the Package Wizard in MTS Explorer.

    Figure 5. Installing package from the Package Wizard

    Client Setup

    The Export process creates a separate subfolder named “clients” in the folder specified in the Export Package dialog box. The Clients folder contains a single .exe file that a user can double-click to run.

    The Client setup merely installs necessary files and registry keys needed by a client to access (remotely through DCOM) your MTS package and its COM servers.

    Remote Deployment and Administration

    The MTS Explorer allows you to manage remote components (those installed on a remote machine). The Remote Components folder contains the components that are registered locally on your local computer to run remotely on another computer. Using the Remote Components folder requires that you have MTS installed on the client machines that you want to configure. If you want to configure remote computers manually using the Explorer, add the components that will be accessed by remote computers to the Remote Components folder.

    Pushing and Pulling

    If both the server and client computer are running MTS, you can distribute a package by “pulling” and “pushing” components between one or more computers. You can “push” components by creating remote component entries on remote computers and “pull” components by adding component entries to your local computer. Once you create the remote component entries, you must add those component entries to your Remote Components folder on your local machine (pull the components).

    Before you deploy and administer packages, set your MTS server up by doing the following:

    • Configure roles and package identity on the system package.
    • Set up computers to administer.

    You must map the System Package Administrator role to the appropriate user in order to safely deploy and manage MTS packages. When MTS is installed, the system package does not have any users mapped to the administrator role. Therefore, security on the system package is disabled, and any user can use the MTS Explorer to modify package configuration on that computer. If you map users to system package roles, MTS will check roles when a user attempts to modify packages in the MTS Explorer.

    Roles

    By default, the system package has an Administrator role and a Reader role. Users mapped to the Administrator role of the system package can use any MTS Explorer function. Users that are mapped to the Reader role can view all objects in the MTS Explorer hierarchy but cannot install, create, change, or delete any objects, shut down server processes, or export packages. If you map your Windows NT domain user name to the System Package Administrator role, you will be able to add, modify, or delete any package in the MTS Explorer. If MTS is installed on a server whose role is a primary or backup domain controller, a user must be a domain administrator in order to manage packages in the MTS Explorer.

    You can also set up new roles for the system package. For example, you can configure a Developer role that allows users to install and run packages, but not delete or export them. The Windows NT user accounts or groups that you map to that role will be able to test installation of packages on that computer without having full administrative privileges over the computer.

    In order to work with a remote computer, you first need to add it to the Computers folder in the MTS Explorer:

    1. Click the Computers folder.
    2. Select New -> Computer from the Action menu.
    3. Enter name of the remote computer.

    Important   You must be mapped to the Administrator role on the remote computer in order to access it from your machine. In addition, you cannot remotely administer MTS on a Windows 95 computer from MTS on a Windows NT server.

    You should now see both My Computer and the new remote computer under the Computers folder. At this point, you can push and pull components between the two machines. Think of the Remote Components folder as its own special package. You are merely adding to it components that exist in one or more packages of remote machines.

    The following example pulls a component from a remote machine to My Computer.

    1. Click the Remote Components folder of My Computer.
    2. Select New-> Remote Component from the Action menu to display the dialog box shown here.

    Figure 6. Adding a component to Remote Components

    In this example, we select (and add) a component called test6.foobar2 from a package called aa on the remote machine calvinh5. This package also has another component (Visual FoxPro OLEPUBLIC class) named test6.foobar, which we do not select. When we click OK, a copy of the DLL and the type library are copied to the local machine (My Computer) and stored in a subfolder of your MTS root location (in this case, c:\ C:\Program Files\Mts\Remote\aa\). In addition, the server is now registered on your machine. Note that while the DLL is copied to your machine, the .dll registered in your registry points to the remote machine.

    If you encounter problems after you click OK, you may not have proper access rights to copy the server components. Ensure that the remote machine is configured with proper access privileges for you. At this point, you can go into Visual FoxPro running on the local machine and access the server:

    oServer = CreateObject("test6.foobar2")
    ? oServer.myeval("SYS(0)")
    

    You use MTS Explorer to view the activated object in the remote machine folder under the package it is registered in. You will not see the object activity in the Remote Components folder. See the “Working with Remote MTS Computers” topic in the MTS Help file for more details.

    Security

    Security in MTS is handled by roles. Roles are established at the package level. Components within that package can set up role memberships. The following MTS Explorer image shows a package called Devcon1, which contains three roles. Only the last two components contain Role Memberships.

    Figure 7. Package with roles

    If you navigate the Roles folder, you can see all Windows NT users or groups assigned to that particular role.

    To create a new role

    1. Click the Roles folder.
    2. Select New-> Role from the Action menu.
    3. Enter a new role name in the dialog box.

    You can add new users/groups to a particular role as follows:

    To add new users or groups

    1. Click the Users folder of the newly added role.
    2. Select New-> User from the Action menu.
    3. Select users/groups from the dialog box.

    MTS handles its security several different ways. The MTS security model consists of declarative security and programmatic security. Developers can build both declarative and programmatic security into their components prior to deploying them on a Windows NT security domain.

    You can administer package security using MTS Explorer. This form of declarative security, which does not require any component programming, is based on standard Windows NT security. This can be done by Package- or Component-level security.

    Declarative Security

    You can manage Declarative security at the package and at the component level through settings available in the Security tab of the Package Properties dialog box.

    Package-level security

    Each package has its own security access authorization, which can be set in the Package Properties dialog box.

    Figure 8. Package properties

    By default, the Security check box is not marked, so you need to check this box to enable security. If you do not enable security for the package, MTS will not check roles for the component. If security is enabled, you must also enable security at the component level in order to have roles checked.

    Component-level security

    Each installed component can also have its own security setting. You set security for a component through the same Enable authorization checking check box on the Property dialog box in MTS Explorer. If you are enabling security at both levels and you do have defined roles, you must include one of the roles in the component’s Role Membership folder. If you do not include a role in the folder, you will get an “Access is denied” error message when you try to access a property or method of the component. Of course, if you do not have any roles, you will get the same error.

    Note   You can still do a CreateObject on the component, but that is all.

    oContext = CreateObject("vfp_mts.mts1")
    oContext.Hello()   && will generate an Access is denied error
    

    To restrict access to a specific component within a package, you must understand how components in the package call one another. If a component is directly called by a base client, MTS checks roles for the component. If one component calls another component in the same package, MTS does not check roles because components within the same package are assumed to “trust” one another.

    When you change the security settings for a particular package or component, you need to shut down server processes before changes can take place. This option is available from the Action menu when Package is selected.

    Programmatic Security

    You can put code in your program to check for specific security access rights. The following three properties and methods from the Context object return information regarding security for that package or component.Expand table

    MethodsDescription
    IsCallerInRoleIndicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
    IsSecurityEnabledIndicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
    SecurityReturns a reference to an object’s SecurityProperty object.

    The following method checks whether the called object is in a particular role. The IsCallerInRole method is useful when the roles are defined, but if your code is generic and doesn’t know the particular roles associated with a component, you must handle this through your error routine.

    PROCEDURE GetRole (tcRole)   
       LOCAL oMTX,oContext,lSecurity,cRole,lHasRole
       IF EMPTY(tcRole)
          RETURN "No Role"
       ENDIF
       oMtx = CREATEOBJECT(MTX_CLASS)
       oContext = oMtx.GetObjectContext()
       IF oContext.IsSecurityEnabled
          THIS.SkipError=.T.
          lHasRole = oContext.IsCallerInRole(tcRole)
          THIS.SkipError=.F.
          DO CASE
          CASE THIS.HadError
             THIS.HadError = .F.
             cRole="Bad Role"
          CASE lHasRole 
             cRole="Yep"
          OTHERWISE
             cRole="Nope"
          ENDCASE
    ELSE
          cRole="No Security"
    ENDIF
       oContext.SetComplete()
       RETURN cRole
    ENDPROC
    

    Advanced users can access the SecurityProperty object to obtain more details on the user for handling security. The Security object offers the following additional methods.Expand table

    MethodDescription
    GetDirectCallerNameRetrieves the user name associated with the external process that called the currently executing method.
    GetDirectCreatorNameRetrieves the user name associated with the external process that directly created the current object.
    GetOriginalCallerNameRetrieves the user name associated with the base process that initiated the call sequence from which the current method was called.
    GetOriginalCreatorNameRetrieves the user name associated with the base process that initiated the activity in which the current object is executing.

    What type of security should you use? Programmatic security offers more power in terms of structuring specific functionality for particular roles. You can use Case statements, as in the previous example, which perform different tasks, depending on the role. Declarative security, on the other hand, can only control access at the component level (not method or lower).

    Changes to Programmatic security, however, require a new build of the component, which may not always be convenient or realistic. Controlling Component-level security for users and roles by using MTS Explorer to turn security on or off gives an administrator greater control. The optimal solution is one with utilizes both declarative and programmatic securities in the most efficient manner.

    Shared Property Manager

    The Shared Property Manager (SPM) MTS resource dispenser allows you to create and share properties across components. Because it is a resource dispenser, all other components in the same package can share information, but information cannot be shared across different packages. For example, if you want to keep a counter to use for generating unique IDs for objects in a package, you could create a Counter property to hold the latest unique ID value. This property would be preserved while the package was active (regardless of object state).

    The SPM also represents an excellent way for an object to preserve its state before being deactivated in a stateless mode (SetComplete). Just-In-Time activation does not affect or reset the state of SPM.

    The following example shows how to use the SPM with Visual FoxPro servers:

    #DEFINE MTX_CLASS        "MTXAS.APPSERVER.1"
    #DEFINE MTX_SHAREDPROPGRPMGR "MTxSpm.SharedPropertyGroupManager.1"
    PROCEDURE GetCount (lReset)
       LOCAL oCount 
       LOCAL oMTX,oContext
       LOCAL nIsolationMode,nReleaseMode,lExists
       oMtx = CREATEOBJECT(MTX_CLASS)
       oContext = oMtx.GetObjectContext()
       oSGM = oContext.CreateInstance(MTX_SHAREDPROPGRPMGR)
       nIsolationMode = 0
       nReleaseMode = 1
       
    * Get group reference in which property is contained
       oSG = oSGM.CreatePropertyGroup("CounterGroup", nIsolationMode,;
    nReleaseMode, @lExists)
       
    * Get object reference to shared property
       oCount = oSG.CreateProperty("nCount", @lExists)
    * check if property already exists otherwise reset
       IF lReset OR !lExists
          oCount.Value = 1
       ELSE
          oCount.Value = oCount.Value + 1
       ENDIF
       RETURN oCount.Value
    ENDPROC
    

    The following settings are available for Isolation and Release modes.

    Isolation mode

    LockSetGet 0 (default)—Locks a property during a Value call, assuring that every get or set operation on a shared property is atomic. This ensures that two clients can’t read or write to the same property at the same time, but doesn’t prevent other clients from concurrently accessing other properties in the same group.

    LockMethod 1—Locks all of the properties in the shared property group for exclusive use by the caller as long as the caller’s current method is executing. This is the appropriate mode to use when there are interdependencies among properties or in cases where a client may have to update a property immediately after reading it before it can be accessed again.

    Release mode

    Standard 0 (default)—When all clients have released their references on the property group, the property group is automatically destroyed.

    Process 1—The property group isn’t destroyed until the process in which it was created has terminated. You must still release all SharedPropertyGroup objects by setting them to Nothing.

    MTS Support for Internet Information Server

    MTS includes several special system packages for use with Microsoft Internet Information Server (IIS). The Windows NT Options Pack 4.0 integrates MTS and IIS more closely. In the future, you can expect even better integration to play a more central role in your Web applications.

    IIS Support

    • Transactional Active Server Pages—You can now run Scripts in Active Server Pages (ASP) within an MTS-managed transaction. This extends the benefits of MTS transaction protection to the entire Web application.
    • Crash Protection for IIS Applications—IIS Web applications can now run within their own MTS package, providing process isolation and crash protection for Web applications.
    • Transactional Events—You can embed commands in scripts on ASP pages, enabling you to customize Web application response based on transaction results.
    • Object Context for IIS Built-In Objects—The MTS object context mechanism, which masks the complexity of tracking user state information from the application developer, now tracks state information managed by IIS built-in objects. This extends the simplicity of the MTS programming model to Web developers.
    • Common Installation and Management—MTS and IIS now share common installation and a common management console, lowering the complexity of deploying and managing business applications on the Web.

    IIS System Packages

    If you use MTS with Internet Information Server version 4.0, the Packages Installed folder contains the following IIS-specific system packages.

    IIS in-process applications

    The IIS In-Process Applications folder contains the components for each Internet Information Server application running in the IIS process. An IIS application can run in the IIS process or in a separate application process. If an IIS application is running in the IIS process, the IIS application will appear as a component in the IIS In-Process Applications folder. If the IIS application is running in an individual application process, the IIS application will appear as a separate package in the MTS Explorer hierarchy.

    IIS utilities

    The IIS Utilities Folder contains the ObjectContext component required to enable transactions in ASP pages. For more information about transactional ASP pages, refer to the Internet Information Server documentation.

    Automating MTS Administration

    Microsoft Transaction Server contains Automation objects that you can use to program administrative and deployment procedures, including:

    • Installing a prebuilt package.
    • Creating a new package and installing components.
    • Enumerating through installed packages to update properties.
    • Enumerating through installed packages to delete a package.
    • Enumerating through installed components to delete a component.
    • Accessing related collection names.
    • Accessing property information.
    • Configuring a role.
    • Exporting a package.
    • Configuring a client to use Remote Components.

    You can use the following Admin objects in your Visual FoxPro code.Expand table

    ObjectDescription
    CatalogThe Catalog object enables you to connect to MTS Catalog and Access collections.
    CatalogObjectThe CatalogObject object allows you to get and set object properties.
    CatalogCollectionUse the CatalogCollection object to enumerate, add, delete, and modify Catalog objects and to access related collections.
    PackageUtilThe PackageUtil object enables installing and exporting a package. Instantiate this object by calling GetUtilInterface on a Packages collection.
    ComponentUtilCall the ComponentUtil object to install a component in a specific collection and import components registered as in-process servers. Create this object by calling GetUtilInterface on a ComponentsInPackage collection.
    RemoteComponentUtilUsing the RemoteComponentUtil object, you can program your application to pull remote components from a package on a remote server. Instantiate this object by calling GetUtilInterface on a RemoteComponents collection.
    RoleAssociationUtilCall methods on the RoleAssociationUtil object to associate roles with a component or interface. Create this object by calling the GetUtilInterface method on a RolesForPackageComponent or RolesForPackageComponentInterface collection.

    In addition, the following collections are also supported.Expand table

    Collection
    LocalComputer
    ComputerList
    Packages
    ComponentsInPackage
    RemoteComponents
    InterfacesForComponent
    InterfacesForRemoteComponent
    RolesForPackageComponent
    RolesForPackageComponentInterface
    MethodsForInterface
    RolesInPackage
    UsersInRole
    ErrorInfo
    PropertyInfo
    RelatedCollectionInfo

    If you want to get a reference to a particular collection, use the GetCollection method. The following example shows, first, getting the collection of packages and, second, getting a collection of all components in the first package:

    #DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oPackages.populate()
    ? oPackages.Count
    oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackages.Item(0).Key)
    oComps.Populate()
    

    Note   The GetCollection method merely returns an object reference to an empty collection. You need to explicitly call the Populate method to fill the collection.

    Collections are case sensitive, as in the following example code:

    oPackages = oCatalog.GetCollection("Localcomputer")   &&fails
    oPackages = oCatalog.GetCollection("LocalComputer")   &&works
    

    Note   Also keep in mind that all MTS collections are zero-based.

    oPackages = oCatalog.GetCollection("LocalComputer")
    oPackages.populate()
    ? oPackages.item[0].name
    

    See MTS Help for more specific language details.

    Visual FoxPro 6.0 is ideally suited for using MTS Automation because of the new Project Manager and Application Builder hooks support.

    Using Visual FoxPro 6.0 Project Hooks

    The MTS samples posted along with this document contain a special Project Hook class designed specially for MTS. This class automatically shuts down and refreshes MTS registered servers contained in that project. One of the issues that developers must consider when coding and testing servers under MTS is repeatedly opening the MTS Explorer to manually shut down processes so that servers can be rebuilt and overwritten. Using a Project Hook nicely automates this process. Here is sample code from the BeforeBuild event, which iterates through the Packages collection shutting-down processes.

    * BeforeBuild event
    LPARAMETERS cOutputName, nBuildAction, lRebuildAll, lShowErrors, lBuildNewGuids
    #DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
    #DEFINE   MSG_MTSCHECK_LOC   "Shutting down MTS servers...."
    LOCAL oCatalog,oPackages,oUtil,i,j,oComps
    LOCAL oProject,lnServers,laProgIds,lcSaveExact
    THIS.lBuildNewGuids = lBuildNewGuids
    oProject = _VFP.ActiveProject
    lnServers = oProject.servers.count
    DIMENSION THIS.aServerInfo[1]
    STORE "" TO THIS.aServerInfo
    IF lnServers = 0 OR nBuildAction # 4
       RETURN
    ENDIF
    WAIT WINDOW MSG_MTSCHECK_LOC NOWAIT
    DIMENSION laProgIds[lnServers,3]
    FOR i = 1 TO lnServers
       laProgIds[m.i,1] = oProject.servers[m.i].progID
       laProgIds[m.i,2] = oProject.servers[m.i].CLSID
       laProgIds[m.i,3] = THIS.GetLocalServer(laProgIds[m.i,2])
    ENDFOR
    ACOPY(laProgIds,THIS.aServerInfo)
    * Shutdown servers
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oUtil = oPackages.GetUtilInterface
    oPackages.Populate()
    lcSaveExact = SET("EXACT")
    SET EXACT ON
    FOR i = 0 TO oPackages.Count - 1
       oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackages.Item(m.i).Key)
       oComps.Populate()
       FOR j = 0 TO oComps.Count-1
    IF ASCAN(laProgIds,oComps.Item(m.j).Value("ProgID")) # 0
    oUtil.ShutdownPackage(oPackages.Item(m.i).Value("ID"))
    EXIT
    ENDIF
       ENDFOR
    ENDFOR
    WAIT CLEAR
    SET EXACT &lcSaveExact
    * User is building new GUIDs, so packages 
    * need to be reinstalled manually
    IF lBuildNewGuids
       RETURN
    ENDIF
    

    This is only one of the many possibilities provided by a Visual FoxPro Project Hook. The MTS Admin objects can save a great deal of time you normally would spend manually setting options in the MTS Explorer.

    Using Visual FoxPro 6.0 Application Builders

    As with the Project Hooks, you might also want to create an Application (Project) Builder that handles registration of Visual FoxPro Servers in MTS packages. The Visual FoxPro MTS samples include such a builder. (See the Readme file in the mtsvfpsample sample application for more details on setup and usage of these files.)

    This Builder simply enumerates through all the servers in your Visual FoxPro project and all the available MTS packages. You can then select (or create) a particular package and registered server to install in that package. Additionally, you can set the Transaction property for each component. The Visual FoxPro code called when the user clicks OK is as follows:

    #DEFINE   MTS_CATALOG      "MTSAdmin.Catalog.1"
    #DEFINE   ERR_NOACTION_LOC   "No action taken."
    LOCAL oCatalog,oPackages,oUtil,i,j,oComps,nPoslcPackage
    LOCAL lPackageExists,oCompRef
    LOCAL oProject,lnServers,laProgIds,lcSaveExact,oPackageRef,lctrans
    lcPackage = ALLTRIM(THIS.cboPackages.DisplayValue)
    lPackageExists = .f.
    SELECT mtssvrs
    LOCATE FOR include
    IF !FOUND() OR EMPTY(lcPackage)
       MESSAGEBOX(ERR_NOACTION_LOC)
       RETURN
    ENDIF
    THIS.Hide
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oPackages.Populate()
    FOR i = 0 TO oPackages.Count-1
       IF UPPER(oPackages.Item(m.i).Name) == UPPER(lcPackage)
          oPackageRef = oPackages.Item(m.i)
          lPackageExists=.T.
          EXIT
       ENDIF
    ENDFOR
    IF !lPackageExists   &&creating new package
       oPackageRef = oPackages.Add
       oPackageRef.Value("Name") = lcPackage
       oPackages.SaveChanges
    ENDIF
    oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackageRef.Key)
    oUtil = oComps.GetUtilInterface
    SCAN FOR include
       oUtil.ImportComponentByName(ALLTRIM(progid))
    ENDSCAN
    oPackages.SaveChanges()
    oComps.Populate()
    SCAN FOR include
       DO CASE
       CASE trans = 1
          lctrans = "Supported"
       CASE trans = 2
          lctrans = "Required"
       CASE trans = 3
          lctrans = "Requires New"
       OTHERWISE
          lctrans = "Not Supported"         
       ENDCASE
       FOR j = 0 TO oComps.Count-1
          IF oComps.Item(m.j).Value("ProgID")=ALLTRIM(progid)
             oCompRef = oComps.Item(m.j)
             oCompRef.Value("Transaction") = lctrans
             oCompRef.Value("SecurityEnabled") = ;
    IIF(THIS.chkSecurity.Value,"Y","N")
          ENDIF
       ENDFOR
    ENDSCAN
    oComps.SaveChanges()
    oPackages.SaveChanges()
    

    Tips and Tricks

    Hopefully, this article offers enough insight into creating Visual FoxPro components that work well with your three-tier MTS applications. Here are a few final tips to consider:

    • Design your components with MTS in mind from the start.
    • Components must be in-process DLLs. Do not use Visual FoxPro EXE servers.
    • When adding Visual FoxPro components, make sure to select both .dll and .tlb files.
    • In the Project Info dialog box of Visual FoxPro DLL servers, set Instancing to MultiUse.
    • Don’t be afraid to mix with other components (for example, Visual Basic servers).
    • You must have DTC running for transaction support.
    • Call SetComplete regardless of whether you’re using transactions, because it places objects in stateless mode.
    • Your MTS object has an associated Context object. Do not place this code in the base client.
    • Connections must have DispLogin set to Never; for SQL pass-through, use SQLSetProp(0).
    • Minimize the number of PEMs on an object (protect your PEMs).
    • Because of page locking issues, limit the length of time you leave SQL Server 6.5 transactions uncommitted.
    • To use security, you must have a valid role associated with the component.
    • Avoid using CreateInstance on non-MTS components.
    • Do not pass object references of the Context object outside of the object itself.
    • Consider using disconnected ADO recordsets to move data between tiers.
    • You can pass Visual FoxPro data in strings, arrays, or ADO recordsets.
    • Passing Parameters:
      • Be careful when passing parameters.
      • Always use SafeArray when passing object references.
      • Passing by value:- Fastest and most efficient- Copies the parameters into a buffer- Sends all values at once
      • Passing by reference:- Sends a reference, but leaves the object back in the client.- Accessing the parameter scampers back to the client machine.
    • Always read the Late Breaking News! It contains important information such as Security configuration details.
    • Visit the Microsoft MTS Web site at www.microsoft.com/com/ for more information.
    • By default, MTS will create a maximum of 100 apartment threads for client work (per package). In Windows NT 4.0 Service Pack 4 (and later), you can tune the MTS activity thread pool. This will not affect the number of objects than can be created. It will simply configure the number that can be simultaneously in call. To tune the MTS activity thread pool:
      1. Open your Windows Registry using RegEdit and go to the package key:HKLM/Software/Microsoft/Transaction Server/Package/{your package GUID}
      2. Add a REG_DWORD named value:ThreadPoolMax
      3. Enter a value for ThreadPoolMax. Valid values are:0 to 0x7FFFFFFF

    The Microsoft Visual FoxPro 6.0 Component Gallery 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. The Visual FoxPro Component Gallery
    3. Understanding Item Types
    4. The BROWSER.DBF Structure

    Steven M. Black

    March 1999

    Summary: Discusses the new Microsoft Visual FoxPro Component Gallery. Covers the open architecture and programmable hooks that the Gallery and the companion Class Browser expose for customization. (17 printed pages)

    Contents

    Introduction The Visual FoxPro Component Gallery Understanding Item Types The BROWSER.DBF Structure

    Introduction

    This white paper discusses the new Microsoft® Visual FoxPro® Component Gallery. Together with the Visual FoxPro Class Browser, the Gallery provides useful ways to accomplish common development tasks. This document also discusses the open architecture and programmable hooks that these tools expose for customization. While this white paper presents the Gallery, an accompanying paper, The Visual FoxPro 6.0 Class Browser, presents the Class Browser, providing details specific to that development tool.

    Update

    The Visual FoxPro 6.0 Component Gallery and other related components have been updated. You can download this update from https://msdn.microsoft.com/vfoxpro/downloads/updates.asp. To ensure that you are using the latest version, you should download and install the update.

    The Component Gallery is the Class Browser’s companion. Both share the same display surface, and you can toggle between them with a handy toolbar button. The Component Gallery can be used to categorize and display almost anything (not just Visual FoxPro components), and its strength is in grouping the various artifacts of software development.

    The Component Gallery is a flexible and programmable shortcut manager and explorer. Since it shows nothing but shortcuts, nothing you do directly in the Component Gallery affects the underlying files. You can create new files through the Gallery, for example, but the element in the Gallery remains a shortcut to the new file. Delete the shortcut and the underlying file is not deleted. (You could extend the Gallery to delete the underlying file when you delete the shortcut, but writing, implementing, and living with this extension is up to you.)

    Visual FoxPro 6.0 uses a system memory variable (named GALLERY) to identify the Component Gallery application. In Visual FoxPro 6.0, the default is GALLERY.APP in your HOME( ) directory. By changing the value of GALLERY, you can wrap and substitute the Component Gallery application just as you do many other Visual FoxPro tools. Visual FoxPro thus continues to provide user-definable extensibility. GALLERY.APP is a wrapper for BROWSER.APP. Running GALLERY.APP is the same as running BROWSER.APP and passing the sixth parameter as true (.T.). You can run the Component Gallery programmatically by using the following code:

    DO (_GALLERY)
    

    The full syntax respected by the Component Gallery includes the following parameters:

    • First parameter (cFileName): File Name or file list (catalog(s) separated by commas).
    • Second parameter (cDefaultItem): Text of Catalog, Folder, or Item to be selected when the Component Gallery is started.
    • Third parameter (nWindowState): Window state of Component Gallery form when started (0=normal, 1=minimized, or 2=maximized).
    • Sixth parameter (lGallery): Must be true (.T.) to run in Component Gallery mode. False (.F.) is for Class Browser mode; refer to The Visual FoxPro 6.0 Class Browser.
    • Seventh parameter (lNoShow): Default false (.F.). If set to true (.T.), the Component Gallery is not activated or shown. You can then use the public memory variable, _oBrowser, to access the Component Gallery object model. (Refer to Visual FoxPro Help for documentation on the Class Browser methods, properties, and events.

    Because of its intimate partnership with the Class Browser, the Component Gallery supports all the add-in mechanisms found in the browser. The Component Gallery, however, adds metadata-driven display and behavior of items to its Class Browser functionality.

    Let’s define a few terms that will help us understand the Component Gallery. After that, we’ll look at how you can use the Component Gallery, then we’ll examine the metadata that makes it all work.

    Here are some terms that will serve us well in subsequent discussion.

    Catalog: The highest-level container in the Component Gallery and its unit of file storage. A catalog is a .DBF or .FPT table whose records define shortcuts to software resources. For example, you could have a catalog named Office Pool.DBF*,* which contained folders and shortcuts to tables, programs, documents, hyperlinks, and anything else needed to manage friendly office wagers. More than likely, you’ll also create project catalogs to organize all the artifacts of your software projects as they are created. Displayed in the Component Gallery, a catalog is a folder with no parent folder.

    Folder: Like a subdirectory, a logical package of items. Catalogs can contain zero or more folders. Folders are either static or dynamic. A static folder contains predefined shortcuts to items. A dynamic folder determines its contents each time the Component Gallery is refreshed. A dynamic folder can be defined as a directory such as “C:\Projects\*.*,” a Visual FoxPro project (.PJX) file, a class library (.VCX) file, a database (.DBC) file, or any URL or file that is valid in the Internet Explorer Web Browser control pane mode.

    Item: A shortcut to a particular artifact.

    Item Type: A category that defines the behavior of the items in the catalog. The default item types are stored in the home( )+”\Gallery\VfpGlry.VCX” class library, and are configurable for each catalog. (For each catalog, see the Properties dialog box, available on the Shortcut menu). The root catalog, always named “Catalogs,” contains the default item types that all catalogs inherit.

    By now you should be comfortable with explorer-type interfaces. If so then the basic features of the Component Gallery work pretty much as you expect.

    The Gallery is divided into two panes. The Catalog pane, on the left, lists the hierarchy of currently open catalogs. The Items pane, on the right, shows the items in the current catalog hierarchy. Both panes provide item-sensitive context menus for doing the usual useful things: cut, copy, paste, rename, and so on. You can also invoke item-sensitive property dialog boxes for selections in the left or right panes. Moreover, the entire Component Gallery is enabled for both regular and OLE drag-and-drop operations.

    In Web view mode, the Items pane is automatically hidden by an Internet Explorer Web browser control. When the selected folder is a dynamic folder that is a URL, the Component Gallery displays in Web view mode. In the Web view mode, the four display control buttons become Back, Forward, Stop, and Refresh buttons.

    As in the Class Browser, you can use the Item icon to drag the currently selected item to the desktop, design surface, or project. Right-clicking Item invokes a GetPict( ) dialog box to change the icon. A nice touch here is that when you select Cancel in the GetPict( ) dialog box, you get an option to reset the icon to the item default. Setting the icon using the Item icon is the same as setting the item picture in the Properties dialog box for that item.

    The View control box contains default and user-defined views of the Component Gallery. Views are queries of specific items in and across catalogs. For example, selecting Internet filters the catalogs to display Internet items only.

    To create your own custom views, see the Dynamic Views tab in the Component Gallery Options dialog box. If Advanced Editing mode is enabled, you can create custom item-type views.

    To create custom item-type views:

    1. Select an item and invoke its properties.
    2. Click the Views tab.
    3. In the EditBox, type a line item such as the following text.My Stuff=Test Files
    4. Press F5 to refresh the Component Gallery.
      The My Stuff view appears in the Views control box.
    5. Select this view, the Test Files folder appears.
    6. Select this folder and a shortcut for that item appears in the right pane.

    This process allows you to create custom item-type views, and any item can be in any view. Refer to the existing item type settings in the shortcut items included in the Visual FoxPro Catalog items.

    The Go to Browser button toggles the Component Gallery window to the standard Class Browser. Hint: right-clicking the button displays a long list of the previously opened folders.

    The Open button is for opening new catalogs. The Open dialog box is a little unconventional and merits explanation. In that discussion, we’ll take our first look at the Component Gallery internals.

    This isn’t your garden-variety Open dialog box. The Catalog box displays the catalogs currently registered on your system. The catalog names are kept in the BROWSER.DBF table, and the detail records for each catalog are stored in the GALLERY\VFPGLRY.DBF table. The Add catalog checkbox adds the contents of the catalog to the current view (the default is “replace”). In addition you can use the Browse button to select an existing catalog that is not listed in the catalog dropdown.

    When you click Options in the Component Gallery window, a three-tabbed dialog box appears wherein you can set certain Component Gallery properties.

    The Standard tab displays the general defaults for the Component Gallery itself; some of these are self-explanatory. Note, however, the Advanced editing enabled checkbox, which enables you to access advanced features of Component Gallery options and property dialogs.

    Use the Catalogs tab to maintain the catalogs that appear in the Catalog box in the Open dialog box. Click New to load a new catalog in the Catalogs pane. A Global catalog is visible in the Catalogs pane regardless of which catalog is selected for display. I’ve made my Favorites catalog a global catalog so I always have access to my favorites. A Default catalog opens whenever you invoke the Component Gallery. Note that when you invoke the Component Gallery from the Class Browser, it always comes up empty. The Component Gallery initially appears populated only when the original invocation is performed with the DO (_Gallery) command.

    The Dynamic Views tab can be used to create your own custom dynamic views of your catalogs. In the figures that follow, I’ve created a new dynamic view named “Excel Spreadsheets” that displays all items of type “file” that contain “.XLS” in their names.

    In the following example, I’ve created a dynamic view of “UseCase” documents by both creating and assigning such keywords as “Actor” and “Extends.” The keywords displayed in this list are stored in a table named Keywords.DBF.

    The Component Gallery Find button is a nice surprise—it works just like dynamic views! In effect, when you use Find you are creating a new persistent view like the one defined on the Dynamic Views tab. I’m not so sure I’m crazy about this. After all, cluttering my own dynamic views every time I search through a file might be a bit much. On the other hand, dynamic views are easy enough to purge in the Dynamic Views tab of the Component Gallery Options dialog box.

    Understanding Item Types

    The behavior of a Gallery item, as in what happens when you click it or drag it, is defined in its item type. The class library, Gallery\VfpGlry.VCX, stores the item types supplied by Microsoft, and you can modify, subclass, or simply copy these classes to create your own types. If you develop your own custom item types, it’s probably a good idea to store them in some other .VCX file, such as My_VfpGlry.VCX. This allows you to later update the Component Gallery class libraries without fear of clobbering your work.

    Here is the hierarchy of the Component Gallery item types supplied by Microsoft. _item and _folder are abstract root classes that are defined in _Gallery.VCX. All the others are in VfpGlry.VCX.

    When creating your own item types, the most flexible prototype is the _fileitem. In fact, _fileitem should serve most of your needs, since it invokes Windows file associations to run or modify the item. Moreover, the _fileitem type can redirect popular file extensions to other file types. We’ll talk more about redirection shortly.

    Item types can be tied to particular catalogs. The root catalog, which is always named “Catalog,” serves as the basis for all catalogs. If you select the Item Types tab on the Folder Properties of the root catalog, you’ll see something like this dialog box.

    Note the following points about the Item Types tab in this dialog box.

    The list of item types matches the item types you see in the New Item shortcut menu. To modify the New Item shortcut menu, simply edit this list.

    Each item type can be associated with display text, a class, and a class library. In this case the display text is “ActiveX,” the class is _ActivexItem, and the class library is VfpGlry.VCX.

    The lines in the properties edit box specify what’s displayed in the Class Item tab of the Item Properties dialog box. For example, the Properties box of the ActiveX item in the Component Gallery example above show the following:

    • File name: (and, within braces, “.ocx, .exe, and .dll”), cFileName—Specifies that the Class Item tab of this class of item will provide a label, textbox, and command button. This information is stored in the object cFileName property.
    • Remote path: (and, within braces, “.ocx, .exe, and .dll”), cRemoteField—Specifies that the Class Item tab of this class of item will provide a label, textbox, and command button. This information is stored in the object cRemoteField property.
    • ActiveX ProgID: cProgID—Specifies that the Class Item tab of this class of item will provide only a label and testbox for ActiveX ProgID. This information is stored in the object ProgID property.

    Note   The entries within braces create a command button with an ellipsis (…) that will, when clicked, display the Open dialog box that defaults to browse for the listed file extensions.

    The other lines of the ActiveX item type are out of view; they include entries for Class, Class library, Source project, and Associated file, and these all work the same as those already described.

    Note   Placing an asterisk (*) before the property name marks that property to be displayed as read-only text in the Properties dialog box. For example, the following entry in Class Item displays the textbox as read-only.

    *Base class:,cBaseClass
    

    If you create your own item types, you can make them available by clicking Add and specifying the new item type.

    Item Redirection

    The Properties page of the _FileItem item type is worth a look because, in addition to showing custom properties, it shows an example of item redirection. See the Redirect box in the following dialog box.

    This is the full list of redirections:Expand table

    APP=_sampleitemAVI=_videoitemBMP=_imageitem
    DBF=_dataitemFRX=_reportitemGIF=_imageitem
    H=_programitem.HTM=_urlitemHTML=_urlitem
    ICO=_imageitemJPG=_imageitemLBX=_reportitem
    LOG=_programitemMNX=_menuitemOCX=_activexitem
    PJX=_ProjectItemPRG=_programitemRMI=_sounditem
    SCX=_formitemTXT=_programitemWAV=_sounditem

    You can probably guess how redirections work: when an item with any of those file extensions is created, the designated item type is created instead. For example, if you try to add a .PRG file as an item, the Component Gallery uses the PRG redirection to create a _programitem instead of a _fileitem. This is why the fileitem item type is so flexible; it has the ability to properly redirect new items to the correct item type.

    The BROWSER.DBF Structure

    The Class Browser stores all its metadata in a table named BROWSER.DBF in your HOME( ) directory. The Component Gallery also uses BROWSER.DBF to store its catalog-related information. Here’s a field-by-field description of important elements in BROWSER.DBF that pertain to the Gallery.Expand table

    FieldDescription
    PLATFORM“WINDOWS” except for records of type “ADDIN” in which the field value is blank.
    TYPE“PREFW” records store browser and gallery preferences. “ADDIN” records store add-in information.
    ID“FORMINFO” records are used by the Class Browser to store form preferences and by the Component Gallery to store information about your catalogs. The only way to tell the difference is that Component Gallery records contain the string “.dbf” in the Name field.”BROWSER” records contain default settings for the Class Browser. See the Properties field for this record to see these default properties.“METHOD” records store Class Browser add-ins that are tied to a particular Class Browser event or method.“MENU” records store Class Browser add-ins that are not tied to a particular Class Browser event or method, and are therefore available on the add-in shortcut menu.
    DEFAULTLogical true (.T.) for the default Component Gallery catalog when the Component Gallery is started with an unspecified first parameter of GALLERY.APP.
    GLOBALApplies to Component Gallery catalog records. Logical true (.T.) if the catalog is global. By default, new catalogs are not global. To specify a catalog to global, select the Catalogs tab in the Component Gallery Options dialog box.
    BACKUPSpecifies, when true (.T.), that the Class Browser or Component Gallery check for duplicate files in the backup subfolder.When a catalog or a VCX is opened by browser/gallery, this field in the associated BROWSER.DBF record is queried. If the backup file doesn’t exist, one is automatically created (including a subfolder named Backup if needed). Then the Backup field is set to false (.F.) You can set this field programmatically to force the browser or gallery to automatically back up that file or table the next time that file is opened, and only the next time.You can set this field via add-in hooks or just at any time with a program that opens and updates browser.dbf.This feature is used internally in one special case. When browser.dbf is first created after VFP is installed, a new browser.dbf, containing the default catalogs (around 5 or so), is created. Because Visual FoxPro does not install the associated backup catalog tables, the Backup field is initially set to true (.T.) so that each catalog is backed up the very first time it is opened. Beyond that special function, its functionality is available to developers for their own purposes.
    NAMESpecifies the file name related to this record. For a Class Browser record, the file type could be, among other things, .VCX, .PJX, .SCX, .OCX, .DLL, .EXE, or APP.For Component Gallery records, the file type is .DBF.In the case of Class Browser and Component Gallery add-ins, the name field stores the name of the add-in. This is what will appear in the add-in shortcut menu if the add-in is not tied to an event or method.
    DESCProvides a description of the catalog referred to in the Name field. Used only by the Component Gallery.
    METHODStores the name of the method to which a Class Browser or Component Gallery add-in is tied. If the method field content equals “*” then the add-in will run for all methods.
    SCRIPTInternal Gallery use only
    PROGRAMUsed by the Class Browser and the Component Gallery to specify the name of the program to run by .PRG-based add-in.
    CLASSLIBUsed by the Class Browser and the Component Gallery to specify the name of the class library in the case of a .VCX-based add-in.
    CLASSNAMESpecifies the name of the class to run in the case of a .VCX-based add-in. Used by the Class Browser and the Component Gallery.
    FILEFILTERSpecifies file masks for which the add-in applies. The FileFilter is specified in the fourth parameter of the Add-in method.
    TOPSpecifies the top coordinate for the browser/gallery form.
    LEFTSpecifies the left coordinate for the browser/gallery form.
    HEIGHTSpecifies the height of the browser/gallery form.
    WIDTHSpecifies the width of the browser/gallery form.
    HEIGHT1Specifies the height of the class and member description panes in the Class Browser.
    HEIGHT2Specifies the height of the item description pane in the Component Gallery.
    WINDOWSTATSpecifies the display size of the Component Gallery or Class Browser.0 – Window is zoomed normal
    1 – Window is minimized
    2 – Window is maximized
    DESCBOXESSpecifies, if true (.T.), that the description panels are to be displayed. Used by the Class Browser and the Component Gallery.
    AUTOEXPANDSpecifies, if true (.T.), that the hierarchical items are automatically to be displayed expanded in the left-hand side pane. Used by the Class Browser and the Component Gallery.
    PUSHPINSpecifies, if true (.T.), that the display is always on top. Used by the Class Browser and the Component Gallery.
    VIEWMODEGallery listview mode (1 – 4).
    FONTINFOSpecifies the Class Browser and the Component Gallery display font preference.
    FORMCOUNTNumber of instances running for file.
    UPDATEDThe date and time this record was last updated.
    COMMENTUnused.
    User1….4Unused.

    The Catalog Table Structure

    This is a very brief overview of Component Gallery-specific metadata. The Component Gallery distributes its metadata to several locations.

    Like the Class Browser, the Component Gallery keeps some of its metadata on a table named BROWSER.DBF, which is found in your HOME( ) directory. The data therein stores the references to the available catalogs, as well as some of their properties such as whether the catalog is a global (auto-open) or default (in the default view). See the BROWSER.DBF metadata description.

    If you delete a Component Gallery catalog record from BROWSER.DBF, it won’t appear in the Component Gallery Open dialog box. The Component Gallery catalog records in BROWSER.DBF contain “.dbf” in the Name field. Since this field is of type memo, you can’t easily identify Component Gallery records in a simple browse of BROWSER.DBF.

    The rest of the Component Gallery metadata is stored in VFPGLRY.DBF, which installs in the Visual FoxPro Gallery subdirectory. This table stores catalog item type metadata. It is here that the behavior of the various item types is defined. When you look at the Component Gallery, you are looking at catalogs whose items are defined in the particular catalog tables, but whose behavior emanates from the items defined here.

    To illustrate some of the functionality of VFPGLRY.DBF, let’s examine fields in a representative record, the one with ID=”fileitem”.Expand table

    FieldValueComment
    Type“CLASS”Metadata class specification. Catalog items can “inherit” from one another. There are thus many different variants of “fileitem” elsewhere in the metadata, and they may override or augment the things defined in this record.The type field can be any value. The values that are reserved and used by the Component Gallery are:“FOLDER”—Folder item (catalog, if parent field is empty).“ITEM”— Item for the right pane, must have a valid parent field setting.“CLASS”—Specifies an item type setting for that specific catalog, beyond the default item types of vfpglry.vcx.“VIEW”—Specifies a custom view used when the catalog is open.“SCRIPT”—Used to specify a special record that contains code in the Script field, and can be called by having a property setting like cDblClick=<MyScript>.“OBJECT”—Used to specify a custom class to be instantiated when the catalog is refreshed. The ItemClass field is used to specify the class name, and the ClassLib field is used to specify the VCX of the class.
    ID“fileitem”The unique identifier for this type of item.
    Text“File”The item display text.
    Typedesc“Item”Specifies the type of element. This is not a folder, but an item.
    Desc The text that appears in the item description pane.
    PropertiesFile name:{},cFileName Parameters:{},cParamsSpecification for input fields that appear in the Properties dialog box for items of this type. Values inside the braces are used as the parameter in GetFile( ) dialogs.
    ClasslibVfpglry.vcxThe class library in which the item’s class is stored.
    Classname_fileitemThe default class that embodies this catalog item.
    ItemtpdescBMP=_imageitem ICO=_imageitem JPG=_imageitem GIF=_imageitem WAV=_sounditem RMI=_sounditem AVI=_videoitem DBF=_dataitem SCX=_formitem MNX=_menuitem FRX=_reportitem LBX=_reportitem PRG=_programitem APP=_sampleitem OCX=_activexitem HTM=_urlitem HTML=_urlitem PJX=_ProjectItem TXT=_programitem LOG=_programitem H=_programitemAlternate classes to embody file items of these particular types. Newly created items with these extensions are remapped to the designated item types.

    Other records may use different fields and different values, but this representative record is enough to get you started in working with the Component Gallery.

    Catalog tables contain records that reference actual catalog items. The main native catalog is named “Visual FoxPro Catalog,” and it is found in VFP_Catalog.DBF. All the Visual FoxPro 6.0 foundation classes, for example, are cataloged there.

    The structure of catalog tables is the same as that of VFPGLRY.DBF, so that much of what we’ve already seen also applies here. This is a good opportunity to look at a few other metadata fields and how they work. This example uses the record with ID=”clireg” in Activex_Catalog.Dbf. This item allows you to register a custom Visual FoxPro automation server remotely, using its generated .VBR file.Expand table

    FieldValueComment
    Type“ITEM” 
    ID“clireg”This item’s ID.
    Parent“actxtools”The ID of the parent catalog record, which refers to a folder named “Tools.”
    Desc“This tool allows you to register a custom VFP automation server remotely using the generated VBR file.”The description window text.
    PropertiescDblClick=<>You can override the events (keypress, click, dblclick, and rightclick) by setting the [cEventName] property. If it’s something like cDblClick=DO foo.prg, then it will run that line. If you set cDblClick=<testscript>, then it will run the code in the Script memo field of the record with ID= “testscript”. If you set cDblClick=<>, then it will run the code in the Script memo field of that record.Thus this DblClick runs the code found in the script field.You can use a record with Type=”SCRIPT” to provide, in the catalog table, a reusable Script memo field that can be called when you run the browser object and reference the .RunScript( ) method.
    Filename(HOME(6)+”CLIREG\CLIREG32.EXE”)The name, stored in oTHIS.cFileName, of the file to run. See the Script field below. Note that the whole behavior of this item is defined by the filename field and, in this case, the Script field. The ClassName and ClassLib fields are blank in this record.
    ScriptcVBRFile = GETFILE(“VBR”) cCliReg = oTHIS.cFIleName IF !FILE(m.cCliReg) RETURN .F. ENDIF IF EMPTY(m.cVBRFile) OR UPPER(JUSTEXT(m.cVBRFile))#”VBR” RETURN .F. ENDIF oTHIS.Runcode([RUN /N &cCliReg. “&cVBRFile.” -NOLOGO])The Script field provides code for the SCRIPT type item with the ID specified by [cEventName] in the properties field. This Visual FoxPro code will run in a code block upon DblClick.Note that in this version there is no script equivalent of DODEFAULT( ), so if you script an event, the default behavior for this event will not execute.If you need a behavior like DODEFAULT( ), just manually make the direct call in the custom script like oTHIS.DblClick

    Steven Black specializes in developing multilingual, multisite, and other challenging software situations, including project turnarounds and cleanups. He is the creator of Steven Black’s INTL Toolkit, a multilingual framework for FoxPro and Visual FoxPro. He’s a regular speaker at Visual FoxPro conferences, and his contributions occasionally darken the pages of FoxPro books and magazines.

    Microsoft Visual FoxPro 6.0 and Visual Studio Installer Tutorial 

    • Article
    • 06/30/2006

    In this article

    1. Tutorial
    2. Create an Application to Distribute
    3. Step 1: Open Visual Studio Installer
    4. Step 2a: Add Application Files to the Installer Project

    Show 9 more

    Microsoft Corporation

    December 1999

    Summary: This article lists the basic steps involved in creating, configuring, and building a Microsoft Windows Installer package (.msi) file with Microsoft Visual Studio Installer. (14 printed pages)Expand table

    Click to download the VFP_VSI.exe sample file.

    Microsoft® Visual Studio® Installer is a graphical tool that simplifies the creation of application setup programs for distribution to single user or enterprise-wide desktops. Setups created with the Visual Studio Installer provide advanced capabilities such as centralized distribution for maintenance and updates, application self-repair, and powerful installation rollback facilities.

    Visual Studio Installer setups are based on the new Microsoft Windows® installer technology. The Windows installer reduces the total cost of ownership (TCO) for customers by enabling them to efficiently install and configure applications. The Windows installer is part of the Windows 2000 and Zero Administration Windows (ZAW) efforts to reduce the overall cost of deploying, using, and managing desktop computers.

    For more information on the Visual Studio Installer, visit the Visual Studio Web site, https://msdn.microsoft.com/vstudio/downloads/tools/vsi11/default.aspx. In addition, you can read the Visual Studio Installer documentation.

    Tutorial

    This tutorial lists the basic steps involved in creating, configuring, and building a Microsoft Windows Installer package (.msi) file with Microsoft Visual Studio Installer. An .msi file is a storage file containing the instructions and data required to install an application.

    This tutorial will show how to author an .msi file to configure the installation of a Visual FoxPro® application. It will also show how to launch the .msi file and install the application.

    To author and launch an .msi file with Visual Studio Installer, complete these tasks:

    1. Open Visual Studio Installer and create an installer project as part of a Visual Studio solution.
    2. Add files to the installer project and configure file properties.
    3. If desired, configure the project properties.
    4. If desired, establish how to modify the target machine system registry when your product is installed and configure registry properties.
    5. If desired, establish how the target machine operating system will handle your installed document types, MIME types, COM objects, and type libraries, and configure properties for each of these objects.
    6. If desired, control and customize the installation dialogs presented when your users run the installer package file to install, repair, or uninstall your product.
    7. Add merge modules to the project.
    8. Build the installer package file.
    9. Test the installer package file.
    10. Distribute the application.

    Create an Application to Distribute

    For the purposes of this demo, the Visual FoxPro Application Wizard was used to create an application called VFPVSIDemo. The application was then built into an EXE, called VFPVSIDemo.exe.

    The data used by the application is in a folder named Data. This folder is a subfolder of the main application folder.

    Step 1: Open Visual Studio Installer

    1. Click Start, and select Programs.
    2. From the Programs menu, select Microsoft Visual Studio 6.0, and then select Microsoft Visual Studio 6.0 Enterprise Tools.
    3. From the Microsoft Visual Studio 6.0 Enterprise Tools menu, click Visual Studio Installer.
    4. The Microsoft Development Environment launches, and you can create a new installer project from the New tab in the New Project dialog box.
    5. Create an empty installer project by highlighting the Empty Installer icon.
    6. Enter VFPVSIDemo as the name of the project.Make a note of the directory in the Location textbox. This is where the application installer file you create will be located. You can change the location for your project if you like.
    7. Choose Open.

    Figure 1. Creating a new Visual Studio Installer project

    Visual Studio Installer creates your installer project. The Project Explorer displays your installer project hierarchy. You can expand the Target Machine node to start setting up the configuration of your installed product on the target machine.

    Figure 2. Empty Visual Studio Installer project

    For more detailed information about creating installer projects, see Creating and Opening Installer Projects.

    Step 2a: Add Application Files to the Installer Project

    The File System editor in Visual Studio Installer gives you a way to configure your application files on the target machine while you add them to the installer project.

    1. In the Project Explorer, expand the Target Machine node.
    2. Double-click File System in the Target Machine node.
    3. In the File System editor, right-click Application Folder.
    4. Select Add File(s) from the context menu.
    5. In the Browse for Files dialog box, navigate to the directory that contains the application. Select the files you want to add. In this case, choose the file VFPVSIDemo.exe.
    6. Click Open.The File System editor displays the file(s) you added in the folder you selected. The files are also listed in the installer project Files node in the Project Explorer.Figure 3. Files added to the installer project
    7. In the File System editor, select User’s Start Menu.
    8. In the Name column, right-click and select Create Shortcut.
    9. In the Shortcut Properties dialog, select VFPVSIDemo.exe and choose OK.
    10. Right-click the shortcut and choose Rename. Rename the shortcut VFPVSIDemo.

    This places a shortcut to the file VFPVSIDemo.exe on the user’s Start menu.

    See the following topics for more detailed information about working with files in an installer project:Expand table

    For information about:See:
    The Visual Studio Installer File System editorFile System Editor
    Adding files to an installer projectAdding Files to an Installer Project
    Adding, moving, or deleting different kinds of files in an installer project and managing the file structure of installer componentsManaging Components, Files, and Folders in an Installer Project
    Setting file propertiesFile Properties

    Step 2b: Add Data Files to the Installer Project

    In the previous step, you added the application files to the installer project. In this step, you will add the data files, which reside in a different directory.

    1. In the File System editor, right-click Application Folder.
    2. Select AddFolder from the context menu.
    3. Change the name of the new folder to Data.
    4. In the File System editor, right-click Data.
    5. Select Add File(s) from the context menu.
    6. In the Browse for Files dialog box, navigate to the directory that contains the application. Select the files you want to add. In this case, choose each of the data files.
    7. Click Open.Figure 4. Data files added to the installer project

    Step 3: (Optional) Configure Project Properties

    1. Select the VFPVSIDemo project in the Project Explorer window.
    2. At the end of the Project menu, select the VFPVSIDemoProperties option.

    The Project Properties dialog box appears. You can view or change the project properties in the Project Properties dialog box.

    For information about the different project properties and how to modify them, see Project Properties Dialog Box.

    Step 4: (Optional) Modify the Target Machine System Registry

    With the Visual Studio Installer Registry editor, you can specify registry values and keys in the target machine system.

    1. In the Project Explorer, expand the Target Machine node under your installer project.
    2. Double-click Registry in the Target Machine node.

    The Registry editor appears.

    See the following topics for more detailed information about manipulating the target machine registry:Expand table

    For information about:See:
    Adding and deleting registry keys and values, as well as setting registry valuesManipulating the Target Machine Registry
    The Visual Studio Installer Registry editorRegistry Editor
    Setting properties for registry entriesRegistry Properties

    Step 5: (Optional) Establish Document and MIME Type and COM Object Associations

    With the Visual Studio Installer Associations editor, you can specify how the target machine operating system will install and register your document types, MIME types, COM objects, and type libraries.

    1. In the Project Explorer, expand the Target Machine node under your installer project.
    2. Double-click Associations in the Target Machine node.

    The Associations editor appears.

    See the following topics for more detailed information about working in the Associations editor:Expand table

    For information about:See:
    Working with document types, extensions, verbs, MIME types, COM objects, and type librariesSetting File, MIME, COM Object, and Type Library Associations
    The Visual Studio Installer Associations editorAssociations Editor
    Configuring properties for document types, file extensions, verbs, COM objects, and type librariesVisual Studio Installer Object Properties

    Step 6: (Optional) Customize the Installation Run-Time Dialog Boxes

    With the Visual Studio Installer User Interface editor, you can customize the installation run-time display. Specifically, you can specify and customize dialogs that are displayed during the installation process.

    1. Open the solution containing your Visual Studio Installer project.
    2. In the Project Explorer, expand the Target Machine node under your installer project.
    3. Double-click User Interface in the Target Machine node.

    The User Interface editor appears.

    See the following topics for more detailed information about installation user interface dialogs:Expand table

    For information about:See:
    Available user interface dialogsInstallation User Interface Dialogs
    The Visual Studio Installer User Interface editorUser Interface Editor
    Adding dialogs to the installer projectAdding Installation Dialogs
    Deleting dialogs from the installer projectDeleting Installation Dialogs
    Customizing available dialogsCustomizing Installation Dialogs
    Working with dialog propertiesUser Interface Dialog Properties

    Step 7: Add Merge Modules

    A merge module (.msm file) is a single package that includes all files, resources, registry entries, and setup logic to install a shared component. Visual FoxPro applications should always include the following merge modules:

    • VFP6RUN.MSM
    • MSVCRT.MSM
    • OLEAUT32.MSM

    The files MSVCRT.MSM and OLEAUT32.MSM ship with Visual Studio Installer. You can find these and other merge modules in the directory c:\Program Files\Microsoft Visual Studio\Common\Tools\VSInst\BuildRes.

    Note   The files contained in MSVCRT.MSM and OLEAUT32.MSM are automatically installed by Windows 2000. Therefore you do not need to add these merge modules to the Installer project if you know the application will only be installed on Windows 2000.

    Save the file VFP6RUN.MSM (available from the sample download at the top of this article) to the directory with the other merge modules.

    1. Choose Add Merge Module(s) from the Project menu.
    2. In the Browse for Merge Module dialog highlight the file VFP6RUN.MSM and choose Open.

    The VFP6RUN.MSM merge module installs and properly registers the Visual FoxPro 6.0 run-time libraries. Refer to Using Microsoft Visual Studio Installer for Distributing Visual FoxPro 6.0 Applications for a reference guide to other available merge modules that ship with Visual Studio Installer.

    Step 8: Build an Installer Package (.msi) File

    After you configure all elements of an application’s installation in your installer project, you must build the project into an installer package (.msi) file. You can then distribute the .msi file to users who want to install your application.

    1. In the Project Explorer, select your installer project.
    2. Make sure the Build type project property (on the Build tab of the Project Properties dialog box) is set to either:
      • Installer
      —or—
    3. With the installer project selected in the Project Explorer, select Build from the Build menu.

    You should see the message Solution Update Succeeded in the Status Bar if the project built successfully. If errors occurred, you should see them in the Task List.

    For more information, see Building an Installer Package (.msi) File.

    Step 9: Test the Installer Package (.msi) File

    For development and debugging purposes, the best way to launch your installer package (.msi) file is from within the Microsoft development environment.

    1. In the Project Explorer window, right-click the VFPVSIDemo project.
    2. Select Launch Installer from the context menu.
    3. Select Next on the opening screen of the VFPVSIDemo Setup Wizard.Figure 5. Opening screen of VFPVSIDemo Setup Wizard
    4. In the Select Installation Folder step, you can choose to install the application in the default directory or change the directory.Figure 6. Select Installation Folder step in VFPVSIDemo Setup Wizard
    5. In the Confirm Installation step, select Next to begin the installation.
    6. When the installation is complete, select Close to exit the VFPVSIDemo Setup Wizard.
    7. Choose VFPVSIDemo from the Start menu to launch the application.

    For more information about these Windows installer requirements and launching an installer package file, see Launching an Installer Package File.

    Note   If you set the Build Type as Installer with Windows Installer Loader in the previous step, you should run SETUP.EXE file to test your setup.

    Run the application to confirm the installation succeeded. If you accepted the defaults, the application is installed in the directory C:\Program Files\VFPVSIDemo and the data is installed in C:\Program Files\VFPVSIDemo\Data.

    Step 10: Distribute the Application

    Your application is now ready for distribution. The file VFPVSIDEMO.MSI contains the application and the files in the VFP6RUN.MSM merge module.

    1. Locate the VFPVSIDemo.msi file. If you accepted the default Location when you created the project, it will be in a directory such as Visual Studio Projects\VFPVSIDemo\Output\DISK_1\.
    2. To launch the installer, double-click the file VFPVSIDemo.msi.
    3. Open the VFPVSIDemo Setup Wizard. Choose RepairVFPVSIDemo to reinstall the application. Choose RemoveVFPVSIDemo to uninstall the application. Then choose Finish.Figure 7. Repair or Remove in VFPVSIDemo Setup Wizard

    Using Microsoft Visual Studio Installer for Distributing Visual FoxPro 6.0 Applications 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. Visual FoxPro Distribution Scenarios
    3. Distributing Your Application
    4. Appendices

    Microsoft Corporation

    December 1999

    Summary: This article is a supplement to assist Microsoft Visual FoxPro 6.0 developers in using the Visual Studio Installer as an alternative to the Visual FoxPro 6.0 Setup Wizard. (13 printed pages)Expand table

    Click to download the VFP_VSI.exe sample file.

    Introduction

    The Microsoft® Visual Studio® Installer is a great new tool you can use to create customized setups for your Visual FoxPro® distributed applications. It is based on the new Microsoft Windows® installer, which reduces the total cost of ownership (TCO) for your customers by enabling them to efficiently install and configure your products and applications. The new Windows installer is part of the Windows 2000 and Zero Administration Windows (ZAW) efforts to reduce the overall cost of deploying, using, and managing desktop computers.

    This article is not meant as a replacement for the Visual Studio Installer (VSI) documentation, which you should read first. It is merely a supplement to assist Visual FoxPro developers in using VSI as an alternative to the Visual FoxPro 6.0 Setup Wizard. We highly recommend you reread the Visual Studio Installer Best Practices section on how to package your application components, available in the VSI documentation.

    Additionally, you should read the accompanying tutorial article, Microsoft Visual FoxPro 6.0 Visual Studio Installer Tutorial, which walks you through the process of creating an installer package with your Visual FoxPro application.

    This article is broken down into sections based on typical distributed application scenarios. At the end, you will find a reference guide to assist you in creating VSI setup scripts.

    Visual FoxPro Distribution Scenarios

    Simple Executable Application

    Many typical Visual FoxPro applications today are built entirely with Visual FoxPro and use native Fox data (that is, .dbc and .dbf files). The following steps are general guidelines to follow for creating a Visual Studio Installer setup for your Visual FoxPro distributed application.

    Note   The accompanying tutorial, Microsoft Visual FoxPro 6.0 Visual Studio Installer Tutorial provides more details if you are not familiar with the basics of using Visual Studio Installer.

    1. Open a new project. Launch the Visual Studio Installer and select a new project of type Empty Installer.
    2. Add application files. You can add files from the Project menu’s Add File(s) item or drag and drop them from the Windows Explorer to either the Project Explorer or File System window. (You cannot drag and drop an entire folder, only files from within.)
    3. Set application file locations. This step ensures that your application files are installed in the proper target location. Use the File System window to place files in the location where you want them installed.Note   If you manually drag and drop files from the Windows Explorer, the application folder structure is not preserved. You need to manually add subfolders to the Application Folder in the File System window to preserve the folder structure.
    4. Set file-specific settings. Open the Properties window to set individual file install settings for any file in your VSI project.
    5. Add a shortcut to application. You can select the User’s Desktop or User’s Start Menu folder in File System window as a place to create a shortcut to your main application. After selecting the desired location, right-click on the right pane and select Create Shortcut. Pick the name of your application from the dialog. If you want, you can add additional shortcuts to other files your application uses.
    6. Add required merge modules. Merge modules are packages of files and install information for common shared components. With Visual FoxPro applications, you should include the following merge modules:
      • VFP6RUN.MSM
      • MSVCRT.MSM
      • OLEAUT32.MSM
      Merge modules that ship with VSI are installed in the following location:C:\Program Files\Microsoft Visual Studio\Common\Tools\VSInst\BuildRes

    Copy

    This location includes MSVCRT.MSM and OLEAUT32.MSM. Merge modules can be added to your project in a way that is similar to how you add a file. The **Project** menu’s **Add Merge Module(s)** item allows you to do this.
    
    > **Note**`   `Windows 2000 installs files in these last two merge modules under System File Protection. If you are only distributing your application to customers running Windows 2000, you do not need to include these modules.
    
    
    1. Set Project Options. You do this through the Project menu’s myproject Properties item (see VSI documentation).Important   Choose the appropriate Build Type option based on your target customer. Picking the Installer with Windows Installer Loader option will add an extra 2.6 megabytes (MB) to the entire setup, but it is required for customers who do not have the Windows Installer loaded on their machines.
    2. Set additional VSI installer options (see VSI documentation).
    3. Build your .msi installer package file by selecting Build from the Build menu.

    The output of your project is a Microsoft Installer package file (.msi), which any user can double-click to run. See the section Distributing Your Application below for more details.

    Executable Application with ActiveX Controls

    A common element of many Visual FoxPro applications is ActiveX® Controls. You can include ActiveX Controls with your VSI setups by following these steps:

    1. Follow steps in the Simple Executable Application scenario above.
    2. Include the COMCAT.MSM merge module. Note that Windows 2000 also installs files in this merge module.
    3. Follow additional steps below based on specific ActiveX Controls being installed:Common ActiveX Controls—these are the common controls, which ship with Visual FoxPro 6.0 and Visual Studio 6.0. VSI ships with merge modules for most of these controls. Simply add the appropriate merge module for that control (see the Reference Guide below). For example, if your application uses the Treeview control, you should include the MSCOMCTL.MSM merge module.VBCCE Controls—Visual Basic® 6.0 allows developers to create custom ActiveX Controls. You will need to manually add this control to the project and set certain properties (for example, install location, registration). You can set the Register property for the ActiveX Control file to vsifrSelfReg (1) to register the file (see Important below for more details). The install location for this control can be the same as the application if the control is not likely to be shared with other applications. In addition to the actual control, you will also need to include the Visual Basic run-time merge module (MSVBVM60.MSM).MFC Controls—some of the older controls, such as the Calendar control, use the MFC libraries. You should include the MFC42.MSM merge module if this is the case. As with VBCEE controls, you will need to manually add the control to the project and set various settings.Third-Party Controls—refer to documentation provided by the vendor on how and where to install the control. Make sure you register the control. The documentation should also provide information on any dependency files needed (for example, MFC, Visual Basic run time). You may also have to add specific Registry keys (use VSI Registry window) for any necessary licensing requirements.

    Important   To ensure the Microsoft Windows installer knows about your installed files to roll back or advertise them, you must install the files in a manner compliant with Windows Installer requirements. Self-registering your files is not compliant with Windows installer requirements. The Associations Editor in Visual Studio Installer makes it possible for you to install Windows installer-compliant COM objects. For more information, see the Visual Studio Installer documentation.

    If you choose to register controls by setting the Register property to vsifrSelfReg, then you should also set the SharedLegacyFile property so that the application can be properly reference counted. This is essential if that control is shared by multiple applications.

    Note   The Common ActiveX Controls included in the VSI merge modules contain registration information that is Windows installer-compliant.

    Applications with HTML Help

    The VFP6RUN.MSM merge module includes both FOXHHELP.EXE and FOXHHELPPS.DLL files needed to support context HTML Help within your Visual FoxPro 6.0 applications. Besides your application specific .chm file, you will need to include the core HTML Help viewer files. The HTML Help viewer files are available as a redistributable called HHUPD.EXE, which can be downloaded from the MSDN Web site https://msdn.microsoft.com/library/tools/htmlhelp/wkshp/download.htm. Users will need to run this HHUPD.EXE installer after they run the .msi installer.

    Applications using MDAC Components

    If your applications use any of the following data components, you will want to include the Microsoft Data Access Components merge module (MDAC.MSM).

    • ODBC Drivers
    • OLE DB Providers
    • ADO
    • RDS

    Important   The MDAC merge module does not actually contain any MDAC files. It simply provides a check for the installer. If MDAC is not installed on the user’s system, then a message dialog is displayed indicating that the user needs to also install these components. You will still need to include the actual MDAC redistribution setup with your application. This setup (MDAC_TYP.EXE) is included at the following Visual FoxPro 6.0 SP3 location:

    <vfproot>\Distrib.src\System\

    You can also obtain MDAC_TYP.EXE from the Microsoft web site. Users will need to run the MDAC_TYP.EXE installer manually after they run the .msi installer.

    Shared Components

    Often, applications consist of components that are shared by multiple applications. Visual FoxPro frameworks, foundation classes, and other shared files and class libraries are examples of these types of components. As described in the Visual Studio Installer documentation, it is recommended that these types of files be combined into merge modules, which can be included later with any application installer package. This ensures that files are always installed in a consistent manner.

    The Visual Studio Installer also lets you combine files (as well as shortcuts, registry keys, and so on) into a common component. However, the Visual Studio Installer best practice recommendation is to make each file included in your installer project a component. One of the limitations of combining multiple files into a single component is that all files must be installed in the same directory on the target machine. A good example of when you might combine multiple files into a single component is Visual FoxPro binary files (for example, dbf/fpt, scx/sct, frx/frt, lbx/lbt).

    COM Servers

    A specific example of components that could be packaged into merge modules is a Visual FoxPro COM Server (both Local .exe and In-Proc .dll servers). You can then add your COM Server merge module to any VSI installer project.

    Correctly installing and registering COM objects is necessary to take advantage of Windows installer rollback and advertising features. As explained in the VSI documentation, two powerful features of the Windows installer are the abilities to:

    • Roll back an unsuccessful installation, returning the target machine to its preinstallation state.
    • Advertise installed products or even individual elements of a product, such as COM objects. Advertising makes a product or COM object available to the user or target machine (by placing a shortcut in the appropriate place, such as the Start menu or registry) without installing the product until the user or another machine function specifically calls the advertised element.

    To support rolling back component installation and registration if your product installation fails and component advertisement on the target machine, you must register installed COM objects by establishing the necessary associations. You can choose not to do so by self-registering your installed COM objects, but this sacrifices the enhanced Windows installer rollback and advertising functionality.

    With traditional scripted setup programs, self-registration was the accepted method for installing COM objects, and it is still a viable method. However, the Windows installer cannot perform rollback installations and registration of self-registered COM objects, and it cannot advertise those objects. This is because self-registered COM objects do not pass their installation and registration information to the Windows installer.

    To ensure that the Windows installer knows enough about your installed COM objects to perform a rollback on or advertise them, you must install those COM objects in a manner compliant with Windows installer requirements. With the Associations editor in Visual Studio Installer, you can install Windows installer-compliant COM objects.

    Additionally, type library information for the COM Server must also be captured because it is also registered in the Registry. With Visual FoxPro, a type library can be bound inside of the .exe or .dll server file, or exist separately as a .tlb file.

    When authoring a VSI setup to include a Visual FoxPro COM Server, you have two options for handling server registration:

    Option 1: You can set the Register property for the COM Server file to vsifrSelfReg (1). This option performs the older style self-registration. However, you lose many of the Windows installer capabilities just mentioned. It is the easiest option for authoring your VSI setup.

    Option 2: The recommended approach for registering COM servers requires a little more work. You will need to manually add Registry keys and COM Object associations to the VSI setup. Let’s walk through an example (note that all the sample files are included with the sample download at the top of this article):

    1. First, add your COM server file(s) to the setup. This is either the .exe or .dll file you created. If you built this file using Windows NT® under Visual FoxPro 6.0 Service Pack 3 (SP3), you do not need to include the .tlb type library file because it is bound into the server file. Otherwise, you need to include this file. After adding these files to the project, make sure to set the Register property to 0 so that they are not self-registered during installation.You can optionally add the server’s .vbr file, which is used by CLIREG32.EXE to register a COM server remotely.Note   The Visual Studio Installer does not support post-install actions as the Visual FoxPro Setup Wizard does, so you need to run CLIREG32.EXE separately after the setup.
    2. Open up the File System window from Target Machine node and select an install location for your server components. The Visual FoxPro 6.0 Setup Wizard typically installs COM servers in the Windows\OleSrv\ folder. You can choose to install to this location if you feel that your component may be shared by multiple applications. If you want to isolate your component specific for a single application, you can install it in the same location as the application.
    3. Author the Registry keys necessary to register the COM server. You can obtain the information to do this by opening up the .vbr file for that server. This file contains all the Registry keys written out when one self-registers a COM server (for example, REGSVR32 foxdemo1.dll). The following .vbr file contents are from a sample COM server called FOXDEMO1.DLL (note that this sample has just one OLEPUBLIC server and is specific for a multithreaded .dll server):VB5SERVERINFO VERSION=1.0.0

    HKEY_CLASSES_ROOT\foxdemo1.foxsvr1 = foxdemo1.foxsvr1 HKEY_CLASSES_ROOT\foxdemo1.foxsvr1\NotInsertable HKEY_CLASSES_ROOT\foxdemo1.foxsvr1\CLSID = {F802CDC0-D690-4603-9936-6860B86A3163} HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163} = foxdemo1.foxsvr1 HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\ProgId = foxdemo1.foxsvr1 HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\VersionIndependentProgId = foxdemo1.foxsvr1 HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\InProcServer32 = foxdemo1.dll HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\InProcServer32\”ThreadingModel” = Apartment HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\TypeLib = {1995B333-9FA9-4819-A320-DA074AB37324} HKEY_CLASSES_ROOT\CLSID{F802CDC0-D690-4603-9936-6860B86A3163}\Version = 1.0 HKEY_CLASSES_ROOT\INTERFACE{DB1D22E5-826B-47DF-95DD-516527BD6E8E} = foxsvr1 HKEY_CLASSES_ROOT\INTERFACE{DB1D22E5-826B-47DF-95DD-516527BD6E8E}\ProxyStubClsid = {00020424-0000-0000-C000-000000000046} HKEY_CLASSES_ROOT\INTERFACE{DB1D22E5-826B-47DF-95DD-516527BD6E8E}\ProxyStubClsid32 = {00020424-0000-0000-C000-000000000046} HKEY_CLASSES_ROOT\INTERFACE{DB1D22E5-826B-47DF-95DD-516527BD6E8E}\TypeLib = {1995B333-9FA9-4819-A320-DA074AB37324} HKEY_CLASSES_ROOT\INTERFACE{DB1D22E5-826B-47DF-95DD-516527BD6E8E}\TypeLib\”Version” = 1.0

    ; TypeLibrary registration HKEY_CLASSES_ROOT\TypeLib{1995B333-9FA9-4819-A320-DA074AB37324} HKEY_CLASSES_ROOT\TypeLib{1995B333-9FA9-4819-A320-DA074AB37324}\1.0 = foxdemo1 Type Library HKEY_CLASSES_ROOT\TypeLib{1995B333-9FA9-4819-A320-DA074AB37324}\1.0\0\win32 = foxdemo1.dll HKEY_CLASSES_ROOT\TypeLib{1995B333-9FA9-4819-A320-DA074AB37324}\1.0\FLAGS = 0

    If you take a close look at the .vbr file, you will see that there are four groups of Registry keys that we need to add. These are keys for PROGIDCLSIDINTERFACE, and TYPELIB. All of the keys fall under the HKEY_CLASSES_ROOT Registry hive. It is a good idea to open up the Registry using REGEDIT to see how the keys should appear. You can later run your .msi setup to see if the Registry keys are being written out properly.

    • PROGID—the PROGID keys are used by COM when you create an instance of the COM server. For example, in the above server, one would call the following:oServer = CreateObject(“foxdemo1.foxsvr1”) The PROGID keys are simply added to your VSI project through the Registry window. Right-click the appropriate node to add new Keys and String Values. You can copy and paste values from the .vbr file to save time.
    • CLSID—the CLSID keys (HKEY_CLASSES_ROOT\CLSID) require a little extra work because you will want to associate your COM server with these keys. You will first add most of the keys similar to the PROGID keys above using the Registry window.Note   A few Registry keys written out during a self-registration are not included in the .vbr file (Implemented Categories and Programmable). The sample includes these. You can also see them in the Registry using REGEDIT.Once you have entered all of the Registry keys except the one with the reference to the actual server file, you need to add a COM Object association for this file using the Associations window. The VSI documentation explains this. Make sure you enter the proper CLSID value.Note   The Windows installer actually adds an extra string value to this Registry key during install. This value may look a little weird, but it is needed by the Windows installer for features such as Advertising.
    • INTERFACE—the Interface keys are entered just like the PROGID keys using the Registry window.
    • TYPELIB—the last set of keys to add are the TYPELIB keys. Again, you will use the Registry window to do this (do not use the VSI Type Libraries option under the Associations window). The one tricky part is entering the name of the file under the WIN32 key because you don’t always know exactly where the file will be installed. The Windows Installer allows you to place custom properties or wildcards in the Value property. For example, you could set the value of the WIN32 key to the following:[TARGETDIR]foxdemo1.dll During the install process, the wildcard placeholder is replaced with the actual value of the folder specific to the target machine. In addition, you will need to use a Windows Installer property for the HELPDIR key. The Appendices below have some of the common Windows installer properties. Refer to the Windows 2000 Platform SDK for more details.

    You are now done and simply need to build your .msi installer package or .msm merge module. It is always a good idea to thoroughly test any setup such as this where you have manually entered Registry keys.

    **Tip   **If you are installing your COM Server onto a Windows 2000 or Windows 98 (Second Edition or later) machine, you should seriously consider installing the component directly to the application directory so that it is isolated from other applications. In order to do this, you must use Option 2 to register your component. The one difference to make in your setup is not to use the Associations window for handling the CLSID registry keys. Instead, you should simply add an entry in the Registry window with the name of your COM server but without a path. If no path is included, COM will first look in the application folder for that component. For more details on isolating components, go to http://search.microsoft.com/us/dev/default.asp and search on “DLL Hell.”

    Localized Applications

    The VFP6RUN.MSM file includes the standard language-neutral resource file (VFP6RENU.DLL), which is used for all English (US) shipping applications. If you want to include support for another localized resource file (VFP6Rxxx.DLL), simply drop that file into the project and install it in the Windows System folder. It does not need to be registered. You should set the File’s DoNotUninstall property to True. For example, include VFP6RDEU.DLL for the German run-time resource file.Expand table

    LanguageResource File
    GermanVFP6RDEU.DLL
    FrenchVFP6RFRA.DLL
    SpanishVFP6RESP.DLL
    Simplified ChineseVFP6RCHS.DLL
    Traditional ChineseVFP6RCHT.DLL

    Unsupported Scenarios

    The Visual FoxPro 6.0 Setup Wizard should still be used for the following scenarios:

    • Applications requiring post-executable actions.
    • Applications requiring Microsoft Graph run-time files.
    • COM Servers requiring DCOM and/or remote automation support (this can be done by manually adding Registry keys, but is not as flexible as Visual FoxPro Setup Wizard).

    Distributing Your Application

    When you distribute your application, you should still follow many of the same guidelines mentioned in the Visual FoxPro documentation. VSI setups (.msi files) use the new Windows Installer technology, which is available on certain platforms. The following steps are general installation instructions when distributing your application to customers. There are two basic scenarios to consider:

    • Installer package built with the Installer with Windows Installer Loader Build Type option. In the near future, this is likely to be the type of installation package you will want to create because many of your customers will still be running on older Windows operating systems. The user simply needs to run the SETUP.EXE file to install the entire application. This bootstrap loader file first checks for and, if necessary, installs the Windows Installer, then it installs your .msi package setup.—or—
    • Installer package not built with the Installer with Windows Installer Loader Build Type option. With these setups, the user already has Windows Installer installed on his or her machine. The user simply needs to run (double-click) the .msi package file to install it.

    Install any additional required setups (users will need to run these separately):

    • MDAC_TYP.EXE—if your application uses any MDAC components.
    • HHUPD.EXE—if your application uses HTML Help.

    Note   Building an installer with the Windows installer bootstrap loader creates these distinct files as part of your installer package. You must include all of these files on the media that you choose to distribute your application:

    • Your .msi file.
    • SETUP.EXE—the file that determines whether or not the Windows installer resides on the target machine and installs the Windows installer if necessary.
    • SETUP.INI—the file that tells SETUP.EXE the name of your .msi file to install.
    • INSTMSIW.EXE—the Windows installer for Windows NT machines. (Windows NT 3.51 is not supported.)
    • INSTMSIA.EXE—the Windows installer for Windows 95 and Windows 98 machines.

    Appendices

    VFP6RUN Merge Module

    The VFP6RUN.MSM merge module properly installs the necessary files to support your Visual FoxPro distributed applications, including COM servers, Active Documents, and normal Windows executables. As with the Visual FoxPro 6.0 SP3 Setup Wizard, run-time files are installed and registered in the Windows System directory. Because the VFP6RUN.MSM merge module is properly authored for Windows installer file installation and registration, it can take advantage of rollback and advertising features. The following files are included in the VFP6RUN merge module:Expand table

    File
    VFP6R.DLL
    VFP6T.DLL
    VFP6RENU.DLL
    VFP6RUN.EXE
    FOXHHELP.EXE
    FOXHHELPPS.DLL

    Reference Guide to VSI Merge Modules

    Expand table

    Core ComponentsMerge Module
    OLE Automation Support FilesOLEAUT32.MSM
    Microsoft Visual C Run-Time LibrariesMSVCRT.MSM
    Microsoft Component Category Manager LibraryCOMCAT.MSM
    Microsoft Foundation ClassesMFC42.MSM
    Visual Basic 6.0 Run-Time LibraryMSVBVM60.MSM
    Microsoft Data Access Components 2.1MDAC.MSM

    Expand table

    ActiveX ControlsMerge Module
    Microsoft Animation Control (v5.0)Microsoft UpDown Control (v5.0)COMCT232.MSM
    Microsoft Coolbar Control (v6.0)COMCT332.MSM
    Microsoft TabStrip Control (v5.0)Microsoft Toolbar Control (v5.0)Microsoft StatusBar Control (v5.0)Microsoft ProgressBar Control (v5.0)Microsoft TreeView Control (v5.0)Microsoft ListView Control (v50)Microsoft ImageList Control (v5.0)Microsoft Slider Control (v5.0)COMCTL32.MSM
    Microsoft Common Dialog Control (v6.0)COMDLG32.MSM
    Microsoft Data Bound Grid Control (v5.0)DBGRID32.MSM
    Microsoft DBList Control (v6.0)Microsoft DBCombo Control (v6.0)DBLIST32.MSM
    Microsoft Multimedia Control (v6.0)MCI32.MSM
    Microsoft Chart Control (v6.0) (OLE DB)MSCHRT20.MSM
    Microsoft Animation Control (v6.0)Microsoft UpDown Control (v6.0)Microsoft MonthView Control (v6.0)Microsoft Date and Time Picker Control (v6.0)Microsoft Flat ScrollBar Control (v6.0)MSCOMCT2.MSM
    Microsoft TabStrip Control (v6.0)Microsoft Toolbar Control (v6.0)Microsoft StatusBar Control (v6.0)Microsoft ProgressBar Control (v6.0)Microsoft TreeView Control (v6.0)Microsoft ListView Control (v6.0)Microsoft ImageList Control (v6.0)Microsoft Slider Control (v6.0)Microsoft ImageComboBox Control (v6.0)MSCOMCTL.MSM
    Microsoft Communications Control (v6.0)MSCOMM32.MSM
    Microsoft FlexGrid Control (v6.0)MSFLXGRD.MSM
    Microsoft Hierarchical FlexGrid Control (v6.0)MSHFLXGD.MSM
    Microsoft Internet Transfer Control (v6.0)MSINET.MSM
    Microsoft MAPI Session Control (v6.0)Microsoft MAPI Message Control (v6.0)MSMAPI32.MSM
    Microsoft Masked Edit Control (v6.0)MSMASK32.MSM
    Microsoft Winsock Control (v6.0)MSWINSCK.MSM
    Microsoft Picture Clip Control (v6.0)PICCLP32.MSM
    Microsoft SysInfo Control (v6.0)SYSINFO.MSM
    Microsoft Tabbed Dialog Control (v6.0)TABCTL32.MSM

    Common Windows Installer Properties

    Expand table

    Property nameBrief description of property
    SourceDirRoot directory containing the source files.
    TARGETDIRLocation into which the installation package is copied during an administrative installation.
    AppDataFolderFull path to the Application Data folder for the current user.
    CommonFilesFolderFull path to the Common Files folder for the current user.
    DesktopFolderFull path to the Desktop folder.
    FavoritesFolderFull path to the Favorites folder for the current user.
    FontsFolderFull path to the Fonts folder.
    NetHoodFolderFull path to the NetHood folder for the current user.
    PersonalFolderFull path to the Personal folder for the current user.
    PrintHoodFolderFull path to the PrintHood folder for the current user.
    ProgramFilesFolderFull path to the Program Files folder.
    ProgramMenuFolderFull path to the Program Menu folder.
    RecentFolderFull path to the Recent folder for the current user.
    SendToFolderFull path to the SendTo folder for the current user.
    StartMenuFolderFull path to the Start menu folder.
    StartupFolderFull path to the Startup folder.
    System16FolderFull path to folder for 16-bit system DLLs.
    SystemFolderFull path to the System folder.
    TempFolderFull path to the Temp folder.
    TemplateFolderFull path to the Template folder for the current user.
    WindowsFolderFull path to the Windows folder.
    WindowsVolumeThe volume of the Windows folder.

    Using MSMQ with Microsoft Visual FoxPro 6.0 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. All About MSMQ
    3. Features of MSMQ
    4. Terminology Overview

    Show 8 more

    Randy Brown
    Microsoft Corporation

    September 1999

    Summary: This article guides Visual FoxPro® developers through successfully writing Visual FoxPro code to access the Microsoft® Messaging Queue (MSMQ) COM objects directly. Many tips and tricks specific to Visual FoxPro are scattered throughout the code samples. (37 printed pages)Expand table

    Click to copy the MSMQwVFP6 sample file.

    Contents

    Introduction All About MSMQ Features of MSMQ Terminology Overview Programming MSMQ with VFP Basic Queue Operations Basic Message Operations Message Acknowledgments Response Messages Advanced VFP Programming for MSMQ Working with MSMQ Transactions Going Forward

    Introduction

    Microsoft® Messaging Queue (MSMQ) is an exciting technology that every Visual FoxPro® developer can employ in his or her application. This document will guide you through successfully writing Visual FoxPro code to access the MSMQ COM objects directly. Many tips and tricks specific to Visual FoxPro are scattered throughout the code samples. Many of the code samples contained in this document are included with the Visual FoxPro MSMQ web pack.

    The first part of this document describes general MSMQ concepts that you need to know. If you are already familiar with MSMQ, you can go directly to the programming sections.

    In preparation for reading this article, you may want to first install MSMQ and read through the documentation. The MSMQ SDK documentation is more detailed and recommended for developers. This article repeats sections from topics in the MSMQ documentation. If you have installed Microsoft Windows® 2000, the Component Services documentation contains expanded detail on messaging services:

    Note   There are differences between MSMQ 1.0 and 2.0 features not documented here, which appear in the Windows 2000 docs.

    MSMQ 1.0 is included as part of the Windows NT4 Options Pack, which is a free download from the Microsoft MSMQ Web site:

    www.microsoft.com/ntserver/appservice/exec/overview/MSMQ_Overview.asp

    MSMQ 1.0 is not installed by default, but rather as an option available in the Custom setup. MSMQ 2.0 will be incorporated directly with future versions of Windows. This document describes MSMQ in a version-independent manner, however, differences between versions, will be noted where applicable.

    Throughout this document, you will see references to e-mail. E-mail and Messaging are often compared and confused. In fact, many people often distinguish between the two with following analogy: E-mail is to People as Messaging is to Applications.

    All About MSMQ

    With the trend toward distributed computing in enterprise environments, it is important to have flexible and reliable communication among applications. Businesses often require independent applications running on different systems to communicate with each other and to exchange messages even though the applications may not be running at the same time.

    MSMQ is a “fast store-and-forward” service for Windows NT Server Enterprise Edition (Windows NT Server/E), that enables applications running at different times to communicate across heterogeneous networks and systems that may be temporarily offline. Applications send messages to MSMQ, and MSMQ uses queues of messages to ensure that the messages eventually reach their destination. MSMQ provides guaranteed message delivery, efficient routing, security, and priority-based messaging.

    Message queuing is like e-mail (asynchronous) versus the telephone (synchronous). See the example in Figure 1:

    Figure 1. Asynchronous versus synchronous communication

    Features of MSMQ

    MSMQ version 1.0 supports the following features:

    • Asynchronous communication
    • Message-Oriented Middleware (MOM) Connectionless messaging. With store-and-forward message queuing, applications aren’t affected by network fluctuations and do not have to establish sessions. Because MSMQ uses a sessionless model at the application level, the sender and receiver don’t need to support the same protocol. MSMQ supports Internet Protocol (IP) and Internet Packet eXchange (IPX).
    • Network traffic prioritization. Message prioritization allows urgent or important traffic to preempt less-important traffic so you can guarantee adequate response time for critical applications at the expense of less important applications.
    • Guaranteed delivery. Messages can be logged to a disk-based queue to provide guaranteed delivery.
    • Transactions. The MSMQ transaction flag can be used to implement transaction-based applications, ensure messages are delivered in order, ensure messages are delivered no more than once, and confirm messages reached or were retrieved from the destination queue.
    • Dynamic queues. Queue information resides in a dynamic/replicated database so administrators can change queue properties without affecting messaging applications. Using MSMQ Explorer, administrators can make these changes from any computer running MSMQ Explorer.
    • Routing. MSMQ supports smart routing, based on the physical topology of the network, session concentration, and transport connectivity. Session concentration allows efficient usage of slow links.
    • Security. MSMQ supports privacy and security through access control, auditing, encryption, and authentication. Access control is implemented using Windows NT security and digital signatures. Auditing is implemented through the Windows NT event logging service. Encryption and authentication (using digital signatures) are supported using public and private keys.
    • Disparate system integration. MSMQ-based applications can be implemented across a wide variety of hardware platforms using MSMQ connectivity products provided by Level 8 Systems.

    Dynamic queues, integrated security, manageable scalability, and smart routing differentiate MSMQ from other middleware implementations available today.

    MSMQ differs from Remote Procedure Calls (RPC), where applications are required to maintain sessions, and from Windows Sockets, and messaging API (MAPI). Although Windows Sockets provides low-level functions for writing applications, Windows Sockets does not allow applications to run at different times in the way that MSMQ does and MSMQ uses a more general-purpose message queuing model than MAPI.

    Terminology Overview

    MSMQ applications communicate between computers using a unit of information (text or binary data) called a messageTransactional messages, those which can be discarded if the transaction is aborted, can be used to pair the sending or receiving of any message with an action in another operation. Using transactional messages ensures that the unit of work is carried out as an atomic operation, that is, the operation succeeds or fails as a whole. Transactional messages can also be used to ensure that a message is delivered only once and that all messages sent from one computer to another are delivered in order. Positive and negative acknowledgements can be used to confirm messages reached or were retrieved from the destination queue. See Figure 2.

    Figure 2. Transactional messages

    MSMQ supports two delivery methods: express and recoverable. Choosing between express and recoverable delivery is a matter of trading performance and resource use for reliability and failure recovery. Express messages use fewer resources and are faster than recoverable messages, but cannot be recovered if the computer storing the memory-mapped message fails. Recoverable messages use more resources and are slower than express messages, but can be recovered no matter which computer fails.

    MSMQ uses public and private queues to store and forward messages. All MSMQ queues, regardless of their function, can be manipulated with the same MSMQ functions. This includes the special journal, dead letter, transactional dead letter, administration, system, and report queues. Each of the queues is simply a standard MSMQ queue used for a specific purpose. For more information on the MSMQ API, see the Microsoft Message Queuing Services Software Development Kit (MSMQ SDK).

    MSMQ routes and delivers messages based on a combination of queue priority and message priority. Messages are routed and delivered by queue priority first, and message priority second.

    MSMQ supports dependent clientsindependent clients, and servers. Independent clients and servers run the MSMQ Services and can communicate asynchronously, while MSMQ-dependent clients require synchronous access to an MSMQ Service.

    Some components of MSMQ Services hold copies of the MSMQ information store (MQIS) database. The MQIS is a distributed database that holds enterprise topology, enterprise settings, computer information, and queue information. MSMQ-based applications can query the MQIS to find queues and get queue properties.

    Note   With MSMQ 2.0, the MSMQ information store can also be Microsoft Active Directory™ services.

    All computers operate within one MSMQ enterprise, divided into sites, and connected through site links. Site link costs define the cost of sending messages between sites, making communication between any two computers fast and inexpensive. Computers running in MSMQ communicate over connected networks (CNs), a collection of computers where any two computers can communicate directly. MSMQ Services designated as in routing servers (InRSs), out routing servers (OutRSs), and site gates can be used to control the flow of messages and provide session concentration. MSMQ Services take all these factors into account when routing messages within your MSMQ enterprise.

    Note   Many of the MSMQ topology concepts have changed with MSMQ 2.0. PECs (Primary Enterprise Controllers) are now Domain Controllers, and the concept of connected networks no longer exists. Make sure to read through these documents carefully.

    Programming MSMQ with VFP

    This section of this article focuses on Visual FoxPro-specific programming practices for integrating MSMQ into your applications.

    Using the MSMQ ActiveX® objects, you can program all common messaging needs including:

    • Creating a message queue
    • Getting a List of Available Queues
    • Opening and deleting queues
    • Sending messages
    • Reading messages
    • Sending messages with acknowledgments
    • Responding to an event triggered when message arrives in queue

    There are various ActiveX objects (COM servers) available for accessing MSMQ, including those for handling queues, messages and events. This article is not a language reference, so not all the available objects, properties and methods are mentioned below. The focus is on specific scenarios with emphasis on real-world sample code. For information on all the MSMQ objects, refer to the Microsoft Message Queuing Services Administrator’s Guide:

    • MSMQQuery
    • MSMQQueueInfos
    • MSMQQueueInfo
    • MSMQQueue
    • MSMQMessage
    • MSMQEvent
    • MSMQApplication
    • MSMQCoordinatedTransactionDispenser
    • MSMQTransaction
    • MSMQTransactionDispenser

    You may not have a need for all of these objects (especially some of the transaction ones), but it’s a good idea to be somewhat familiar with them.

    In addition to the core set of objects, MSMQ also provides a rich set of automation components that support composing and parsing the body of MSMQ mail messages, which are used to communicate with e-mail based applications through the MSMQ mail services. These include objects such as MSMQMailEMailMSMQMailFormData, and MSMQMailRecipient. See the MSMQ SDK docs for more details.

    Before you begin using VFP and MSMQ, read some of the comments in source code. They contain valuable tips and idiosyncrasies with using the MSMQ objects.

    Important   It is highly recommended that you use Visual FoxPro 6.0 SP3 or higher when programming against MSMQ. There are API functions that require this version.

    Listed below are a few examples of why you should incorporate MSMQ into your applications.

    • Applications that are often disconnected – many Fox developers today are creating distributed multi-tier applications. With these types of applications, it’s sometimes the case where client machines are taken offline from the host server machine(s). This can apply to both Web or LAN based situations. One of the big advantages of MSMQ is that it handles both offline and online scenarios transparently, saving you from writing separate offline and online application logic.Think of this being similar to your e-mail client. You can write e-mail messages both offline and online without any behavioral distinction.
    • Asynchronous messaging – have you ever written a Visual FoxPro application where one of the application operations takes a long time to perform (e.g. printing a report, re-indexing a large database, etc.)? Visual FoxPro does not return control to the user until the operation is completed. This can be very annoying to the end user who is trying to be productive with your application. Many Visual FoxPro developers employ kludges to handle these situations such as Timers and DO WHILE loops which continuously poll some resource (e.g., file or table) for a change (semaphore) made by the application. These types of coding habits are not efficient and needlessly waste valuable system resources and processor time. With asynchronous messages, you can setup a VFP object that sits in limbo until a message arrives in a queue. Once this event happens, a method on your VFP object is called. You can call a VFP COM server, which runs in its own process, to handle the application request and allow the user to continue working. The MSMQ Message can contain all the information being passed from your application to your VFP COM “helper” server.
    • Workflow type applications – typical office settings today require efficient passing of information such as documents between various members of the organization. Along the way, this information is often manipulated and refined. With MSMQ, you can keep a nice audit trail and information can be passed in a secured fashion with guaranteed delivery. Because messages can be sent in a transaction, they will be protected from various risks to the system. See the Response Messages section below for more details.

    Basic Queue Operations

    Creating a Message Queue

    One of the first tasks you will want to do with MSMQ is to create a queue so that you can store messages. The simplest way to do this is by using the MSMQ explorer.

    1. In the MSMQ Explorer, right-click a computer node and select New -> Queue.
    2. In the dialog, enter a name for your queue. You will need to decide whether to make it transactional.

    Programmatically, you can create a queue with just a few lines of code. Here is the basic code you need to create a public message queue.

    lcQueueName = “MyQueue1”
    oQueueInfo = CreateObject(“msmq.msmqqueueinfo”)
    oQueueInfo.Pathname = “.\”+lcQueueName  &&must be unique
    oQueueInfo.Label = lcQueueName
    oQueueInfo.Create
    

    The important Pathname property controls the name and location of the queue. Because it is based on UNC naming conventions, you can include the computer name, or for local queues, use the “.\” qualifier. Alternately, you can use the FormatName property instead of Pathname. The FormatName property is the recommended strategy because it is better suited for offline work. You should be getting a reference to the QueueInfo object. There is a QueueInfos object that represents a collection of QueueInfo objects. Think of a QueueInfo as simply an object containing specific information about the Queue itself such as location. However, there is one important distinction, a QueueInfo does not contain any information about the contents of the queue. The information about the contents is stored in the Queue object. The Queue Query object, which is used to locate a specific queue, returns a QueueInfos object, distinguishing it from possible multiple queues with that same name existing within the enterprise.

    **Important   **To create a public message queue, you must be online (attached to a valid MSMQ Service (e.g., primary site controller). Independent clients cannot create public queues while offline.

    Before creating a public queue, check to see if it already exists by using the following syntax.

    lcQueueName = “myqueue1”
    
    * Try to locate queue first
    oQuery = create(“msmq.msmqquery”)
    
    * Lookup queue to see if it exists
    * Important - queue names are case-sensitive
    oQueueInfos = oQuery.LookupQueue(,,lcQueueName)
    
    * Move to first record in queue set
    oQueueInfo = oQueueInfos.Next()
    
    IF ISNULL(oQueueInfo)  &&queue not yet created so create it
       oQueueInfo = CreateObject(“msmq.msmqqueueinfo”)
       oQueueInfo.Pathname = “.\”+lcQueueName
       oQueueInfo.Label = lcQueueName
       oQueueInfo.Create
    ENDIF
    

    You may need to create a private queue for a variety of reasons, including performance and offline usage. A private queue is named similarly to that of a public queue except it has a “PRIVATE$\” qualifier immediately preceding the name of the queue.

    oQueueInfo.Pathname = “.\PRIVATE$\”+lcQueueName  &&must be unique
    

    Deleting a Message Queue

    While not necessarily a common operation, you may need to delete a particular queue. The following code snippet has this operation.

    IF MESSAGEBOX(“Delete queue?”,36+256)=6
       oQueueInfo = create(“msmq.msmqqueueinfo”)
       oQueueInfo.FormatName = cFormatName
       oQueueInfo.Delete
       RETURN
    ENDIF
    

    The QueueInfo’s PUBLIC format name is a unique identified key such as following:

    PUBLIC=179446c5-0001-11d3-8234-00c04f984590
    

    Getting a reference to a specific queue involves just two lines of code. This is an important concept to remember, that is, obtaining a reference to a specific queue whether it be for deleting, opening it up to send a message, opening it up to read a message, etc.

    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.FormatName = cFormatName
    

    Opening a Queue

    Queues are like tables with the messages being similar to records. Think of them also like cursors in that you can have many instances of the same queue open. The QueueInfo is 1:1 relation and points to the Queue object itself. A queue can be opened to either send messages, read (peek) messages, or retrieve (receive) messages. A queue can not be opened to both send and read a message with the same Open() call. However, you can open multiple instances of the same queue in different modes.

    To open a queue, you use the QueueInfo Open() method:

    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    IF EMPTY(oSendQueue.IsOpen)
       RETURN
    ENDIF
    

    You are probably wondering about the checking here with IsOpen. You should always check to see if your Open operation succeeded. The queue may be unavailable or exclusively in use. The EMPTY() function is used because some versions of MSMQ return logical while others return 0/1.

    The source code includes an MSMQ.H file which is the #INCLUDE file for all the MSMQ constants. These constants are stored in the MSMQ type library. The samples include this file, but can alternately be recreated using the following code (you will need to have VB installed for the special COM server library that reads type libraries).

    * GETCONSTANTS.PRG
    LOCAL lctlbfile, oTLB, oTInfos, lcNewfile, i, j, lnTmpValue
    
    * MSMQ specific files
    lctlbfile = “c:\winnt\system32\mqoa.dll”
    lcNewFile = HOME()+”msmq.h”
    
    oTLB=create(“tli.tliapplication”)
    oTInfos=oTlb.TypeLibInfoFromFile(lctlbfile)
    
    SET TEXTMERGE ON NOSHOW TO (lcNewFile)
    \\* Constants for type library: <<lctlbfile>>
    \* <<oTInfos.HelpString>>
    \
    FOR i = 1 TO oTInfos.Constants.Count
       \* <<oTInfos.Constants[m.i].Name>> Enum
       \* <<oTInfos.Constants[m.i].HelpString>>
       FOR j = 1 TO oTInfos.Constants[m.i].Members.Count
          \#DEFINE 
          \\<<oTInfos.Constants[m.i].Members[m.j].Name>>    
          lnTmpValue = oTInfos.Constants[m.i].Members[m.j].Value    
          \\<<lnTmpValue>>
       ENDFOR
       \  
    ENDFOR
    
    SET TEXTMERGE TO
    SET TEXTMERGE OFF
    MODIFY FILE (lcNewFile) NOWAIT
    

    Getting a List of Available Public Queues

    You may have a need to get a list of all public queues available (independent clients need be online). The following code snippet populates an array aQueues with pathnames of public queues available.

    * Create/locate queue
    LOCAL oQuery,oQueueInfo,oQueueInfos,aQueues
    DIMENSION aQueues[1]
    
    oQuery = create(“msmq.msmqquery”)
    oQueueInfos = oQuery.LookupQueue()
    
    * Move to first record in queue set
    oQueueInfo = oQueueInfos.Reset
    oQueueInfo = oQueueInfos.Next
       
    DO WHILE !ISNULL(oQueueInfo)
       IF !EMPTY(aQueues[ALEN(aQueues)])
          DIMENSION aQueues[ALEN(aQueues)+1]
       ENDIF   
       aQueues[ALEN(aQueues)] = oQueueInfo.pathname
       oQueueInfo = oQueueInfos.Next
    ENDDO
    

    Getting List of Available Private Queues

    Obtaining a list of private queues is a little less intuitive, and the objects themselves do not support specific language to do this. The following work-around seems to handle this fine with MSMQ 1.0 and 2.0 (Microsoft does not support this strategy so use it at your risk).

    #DEFINE MSMQ_KEY         SOFTWARE\Microsoft\MSMQ\Setup”
    #DEFINE HKEY_LOCAL_MACHINE   2147483646
    
    LOCAL oQuery, QueueInfo, QueueInfos, aQueues, i, j, aKeys
    LOCAL aFiles,lcQueue,lcMSMQPath, oReg, oIni
    DIMENSION aQueues[1]
    DIMENSION akeys[1,1]
    DIMENSION aFiles[1]
    
    oReg = NewObject(“registry”,”ffc\registry.vcx”)
    oIni = NewObject(“oldinireg”,”ffc\registry.vcx”)
    IF oReg.EnumOptions(@akeys, MSMQ_KEY, HKEY_LOCAL_MACHINE, .F.) = 0
      FOR i = 1 TO ALEN(akeys,1)
        IF ATC(“Directory”,aKeys[m.i,1])#0
          lcMSMQPath = akeys[m.i,2]+”\Storage\LQS\”
          ADIR(aFiles, lcMSMQPath + “*.*”)
          FOR j = 1 TO ALEN(aFiles)
            lcQueue = ““
            oIni.GetIniEntry(@lcQueue,”Properties”,;
                “QueueName”,lcMSMQPath+aFiles[m.j])
            IF ATC(“private$”,lcQueue)#0
              IF !EMPTY(aQueues[ALEN(aQueues)])
                DIMENSION aQueues[ALEN(aQueues)+1]
              ENDIF
              aQueues[ALEN(aQueues)] = “.”+lcQueue
            ENDIF
          ENDFOR
          EXIT
        ENDIF
      ENDFOR
    ENDIF
    

    Basic Message Operations

    Once you have your queue created, you are going to want to create or read messages in it. By far, these are the most common things you do with messaging, just as you would do with e-mail.

    Creating/Sending a Message

    We now have our queue open specifically for sending a message. Remember, each object reference returned by the Open method can either be used for sending, peeking or receiving a message. However, you can have multiple concurrent object references, each performing a different function. Let’s go ahead and create a simple message.

    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “My First Message”
    oMsg.Body = “This is body for my first message.”
    oMsg.Send(oSendQueue)
    

    In this example, we compose a message that is not dependent of any queue. The body of the message is text we want to post. Later, we will look at alternative content for the message body. When we are finished composing the message, we send it to a queue that we already have opened for sending messages. Sending messages is made simple using this core set of code, but there are lots of options/attributes that can be used.

    Sending Offline Messages

    If you are running an Independent Client machine that is temporarily offline, or the machine with the destination queue unavailable (e.g., offline), then you will need to handle opening a queue a little differently than the typical way using Pathname:

    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.Pathname = “randybr9\myQueue1”
    

    Earlier in this document, the FormatName was described as an alternative approach to using Pathname. If you are sending a message to a public queue while offline (even to your own machine), you must use the FormatName when getting a reference to the queue.

    Note   For private queues, it is fine to use Pathname.

    The reason for using FormatName is that MSMQ needs to access the MSMQ Services when getting information about a public queue to route the message (in MSMQ 1.0, this routing info is stored in a Microsoft SQL Server™ database called MQIS). In offline mode, this information is unavailable, so the queue cannot be opened.

    This concept is similar to working with e-mail offline. If you have your Microsoft Outlook® client opened offline and send a message, the message sits in limbo until you connect to your network where the e-mail server is available to resolve and provide proper routing information. The process of sending a message, when all information for proper routine is unavailable, is known in messaging lingo as Store and Forward.

    MSMQ provides this same service if your MSMQ Service is unavailable to route the message to a public queue. Instead of using the Pathname for queue specification, you use the FormatName. MSMQ supports several different types of Format Names for use. My preference is the Direct Format Name type (see MSMQ reference for details on other types).

    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.FormatName = “DIRECT=OS:randybr9\myQueue1”

    Here are some more examples of Direct Format Names:

    “DIRECT=TCP:157.18.3.1\MyQueue”
    “DIRECT=OS:elvisp.ms.com\myqueue”
    “DIRECT=OS:randybr2\PRIVATE$\myqueue”
    

    If using the Direct Format Name, MSMQ will not try to access queue information from the MQIS database. The message is in essence sent blindly to an address that may or may not exist. Until the machine is back online, that address cannot fully be resolved. This strategy can also improve performance. MSMQ will periodically check to see if you are online so that it can process the outgoing queues.

    Checking if you are Offline

    It’s a good idea if you are working with Independent Clients to have a check in your code to see if MSMQ is online/offline. Again, MSMQ does not support specific language for this, so you can try the following code:

    * This program checks to see if MSMQ is offline so
    * demos will use Private queues
    LOCAL x
    x = create(“checkoffline”)
    RETURN x.IsOffline()
    
    DEFINE CLASS checkoffline AS custom
    PROCEDURE IsOffline
       LOCAL lcQueueName,oQuery,oQueueInfos,oQueueInfo
       lcQueueName = ““
       oQuery = create(“msmq.msmqquery”)
       oQueueInfos = oQuery.LookupQueue
       oQueueInfo = oQueueInfos.Reset
       oQueueInfo = oQueueInfos.Next
       RETURN VARTYPE(oQueueInfo)#”O”
       
    PROCEDURE Error(p1,p2,p3)
    ENDDEFINE
    

    You should make sure you have a routine like this to determine if you can open a queue using Pathname or Formatname.

    Reading a Message

    Once your message queue starts to fill up with messages, you’ll probably want to read (handle) them. It is important that you understand the difference between reading (peeking) at messages vs. retrieving (receiving) them.

    MSMQ offers 2 options for viewing messages, the primary difference being the resulting action after processing of the message. If you Peek at a message, it is left in the queue. If you Receive a message, it is removed from the queue. The idea behind the later is that you are processing the message once and only once. These concepts are important because MSMQ guarantees delivery and is transactional. Acting on the same message twice can cause a redundant transaction. Again, you need to open a queue specifically for either of these types of message viewing.

    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    oPeekQueue = oQueueInfo.Open(MQ_PEEK_ACCESS, MQ_DENY_NONE)
    

    Let’s first look at message peeking. When you first open your queue for peeking, you need to see if any messages exist in the queue. The following code snippet shows this.

    oMsg = oPeekQueue.Peek(,,100)
    IF ISNULL(oMsg)
       ? “Queue is empty”
    ENDIF
    

    There are three Peek functions you can use (Peek, PeekCurrent, and PeekNext).

    Note   The documentation for these three functions for MSMQ 1.0 is incorrect in terms of the ordering of the parameters. The nReceiveTimeout parameter is actually the last parameter.

    PEEK([lWantsDesintationQueue],[lWantsBody],[nReceiveTimeout])
    

    The default for the nReceiveTimeout parameter is infinity, causing your machine to appear hung if it is trying to find a message in an empty queue. This documentation bug is also prevalent with the Receive and ReceiveCurrent functions. You can use the following code to iterate through all of the messages in your queue.

    oMsg = oPeekQueue.PeekCurrent(,,100)
    DO WHILE !ISNULL(oMsg)
       ? oMsg.Body,oMsg.Class
       oMsg = oPeekQueue.PeekNext(,,100)
    ENDDO
    oPeekQueue.Close
    

    Again, since we are only peeking at messages, none of the messages will be deleted from the queue. You can see them in the MSMQ Explorer. The next example shows actually Receiving messages for processing.

    nMessages = 0
    oMsg = oRecQueue.ReceiveCurrent(,,,100)
    DO WHILE !ISNULL(oMsg)
       nMessages = nMessages+1
       oMsg = oRecQueue.ReceiveCurrent(,,,100)
    ENDDO
    ? “Total messages:”,nMessages
    oRecQueue.Close
    

    If you run this code on your message queue, you will notice that the messages no longer appear in the Queue under MSMQ Explorer. Again, this is because Receiving messages causes their removal from the queue.

    Using a Journal

    You’re probably thinking that having messages removed from the queue is a bad thing since there is no way to trace their activity. And it’s likely that you want some sort of activity (audit) log for your application. MSMQ makes it simple to do this. When you create your public/private queue, you can automatically have an associated Journal queue created at the same time (or later on).

    oQueueInfo.Journal = 1 
    

    A Journal is simply an associated queue for processed messages. When a message is Received (via Receive or ReceiveCurrent), it is automatically processed and moved to the Journal queue. You can view the Queue’s Journal queue in the MSMQ Explorer by expanding the Queue’s tree node.

    In fact, you can also programmatically read through the messages in a particular Journal:

    * cFormatName is derived from original Queue (see above)
    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.FormatName = cFormatName+”;JOURNAL”
    oJournalQueue = oQueueInfo.Open(MQ_PEEK_ACCESS,MQ_DENY_NONE)
    oMsg = oJournalQueue.PeekCurrent(,,100)
    DO WHILE !ISNULL(oMsg)
       oMsg = oJournalQueue.PeekNext(,,100)
       ? oMsg.Body,oMsg.Class
    ENDDO
    oJournalQueue.Close
    

    Message Acknowledgments

    Sending a Message with Acknowledgment

    We’ve now added some simple messages to a queue and even found a way to read/process them. No doubt you’ve sent e-mail to a friend in the past and never received an acknowledgment. While this might be fine for simple e-mail, often with your application, you are going to want some sort of acknowledgment that the message was received.

    MSMQ offers two types of acknowledgments.

    • When message reached queue (MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE)
    • When message was actually read (MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE)

    As with most operations involving MSMQ, acknowledgments are handled via sending a new message to a queue. The only difference with an Ack message is that there is no attached body. A special queue is setup for the acknowledgment message. This is referred to as an Admin Queue. An Admin Queue is actually just a normal queue (you can have a Journal associated with it), and Ack messages which you process (Receive) are removed (and optionally forwarded to associated Journal). An Admin Queue is created in the normal fashion like any other queue (see above).

    The primary difference with message acknowledgments is not in the Admin Queue, but rather in the composition of the message itself. Here is an example of a message being sent with instructions for acknowledgment.

    * Create new messages
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    IF EMPTY(oSendQueue.IsOpen)
       RETURN
    ENDIF
    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “Test Message”
    oMsg.Body = “This is a sample message.”
    oMsg.Ack = MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE
    *oMsg.Ack = MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE
    oMsg.AdminQueueInfo = oAdminQueueInfo
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    The only difference with a normal message is setting of the Ack and AdminQueueInfo properties.

    Note   Setting of AdminQueueInfo property requires Visual FoxPro 6.0 SP3 or higher.

    Reading Message Acknowledgments

    You are going to want to check to see whether all of the messages reached their target queue, were received within a timeout period, etc. This is all handled by the Class property of the message.

    oRecQueue = oAdminQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    oMsg = oRecQueue.PeekCurrent(,,100)
    IF !ISNULL(oMsg)
      DO CASE
      CASE oMsg.Class = MQMSG_CLASS_ACK_REACH_QUEUE
       MESSAGEBOX(“The message reached the queue.”)
      CASE oMsg.Class = MQMSG_CLASS_ACK_RECEIVE
       MESSAGEBOX(“The message was acknowledged as received.”)
      CASE oMsg.Class = MQMSG_CLASS_NACK_RECEIVE_TIMEOUT
       MESSAGEBOX(“Message was not removed from the queue in time.”)
      OTHERWISE
       MESSAGEBOX(“The message did not reach the queue.”)
      ENDCASE
      oMsg = oRecQueue.Receive(,,,100)  &&remove from queue
    ENDIF
    oRecQueue.Close
    

    Message Timeouts

    If you are receiving Ack messages based on when the target message queue actually processes (ReceiveReceiveCurrent) the message, you can also impose a timeout. This is handled by the message’s MaxTimeToReceieve property.

    If the time-to-be-received timer expires before the message is removed from the queue, MSMQ discards the message, sending it to the dead letter queue if the message’s Journal property is set to MQMSG_DEADLETTER.

    MSMQ can also send a negative acknowledgment message back to the sending application if the message is not retrieved before the timer expires.

    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “Test Message”
    oMsg.Body = “This is a sample message.”
    oMsg.Ack = MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE
    oMsg.MaxTimeToReceive = 20
    oMsg.AdminQueueInfo = oAdminQueueInfo
    oMsg.Send(oSendQueue)
    

    There is also a similar MaxTimeToReachQueue property.

    Note   If the queue is local, the message always reaches the queue.

    These two Acknowledgment operations are facilitated by MSMQ’s built-in Message Timers.

    More About Message Timers

    MSMQ provides two timers to help you maintain better control of your messages: a time-to-be-received and a time-to-reach-queue timer.

    The time-to-be-received timer determines how long a message remains in the system, starting from the time the message is sent, to the time it is removed from the target queue.

    The time-to-reach-queue timer determines how long a message has until it reaches the target Queue Manager of the target queue. Typically, this timer is set to a value less than the time-to-be-received setting.

    When both timers are used, and if the time-to-be-received timer is set to a shorter time interval than the time-to-reach-queue timer, it takes precedence over the time-to-reach-queue timer. MSMQ does not allow messages to remain in the system longer than the time allowed by their time-to-be-received timer.

    When either timer expires, MSMQ discards the message. However, MSMQ can also send a copy of the message to a dead letter queue or an acknowledgment message to an administration queue. If the message’s acknowledgment property specifies full or negative acknowledgments, MSMQ sends the appropriate negative acknowledgment message to the administration queue specified by the message. If the message’s journal property specifies a dead letter queue, a copy of the message is sent to one of two places. The copies of non-transactional messages are sent to the dead letter queue on the computer where the timer expired. Copies of transactional messages are copied to the transactional dead letter queue on the source machine.

    Response Messages

    Message acknowledgments are convenient for many application needs, especially those involving workflows. You might also want to consider a more detailed approach using an actual response message. Similar to the Admin Queue used for Ack messages, you will need to set up a special Response Queue for Response messages. A major difference with Ack messages is that the Response message contains a body. In reality, the Response Queue is nothing fancy and could easily be handled without the built-in functionality.

    Sending Message with Response

    Sending a message that asks for a response requires use of the ResponseQueueInfo property of your outgoing message.

    * Create new messages
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “Test Message”
    oMsg.Body = “This is a sample message.”
    oMsg.Delivery = MQMSG_DELIVERY_RECOVERABLE
    oMsg.ResponseQueueInfo = oResponseQueueInfo
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    Reading Message from Response Queue

    Messages can be read from the Response Queue. When a message arrives at its target queue, you can query the ResponseQueueInfo property to get an object reference to the Response Queue. Once you have this, you simply create a new message (with body) to send.

    * Read message from target queue
    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    oMsg = oRecQueue.Receive(,,,100)
    IF ISNULL(oMsg)
       RETURN
    ENDIF
    
    * See is a reponse queue is setup and send message if so
    IF !ISNULL(oMsg.ResponseQueueInfo)
       oRespQueue = oMsg.ResponseQueueInfo.Open(MQ_SEND_ACCESS,;
       MQ_DENY_NONE)
       oMsg2 = create(“msmq.msmqmessage”)
       oMsg2.Label = “Response Message”
       oMsg2.Body = “This is a response message”
       oID = oMsg.id
       cTmpStr=““ 
       FOR i = 1 TO ALEN(oID)
          cTmpStr = cTmpStr+CHR(oID[m.i])
       ENDFOR
       oMsg2.CorrelationId = CreateBinary(cTmpStr)
       oMsg2.Send(oRespQueue)
       MESSAGEBOX(“A response message was returned.”)
       oRespQueue.Close
    ENDIF
    

    Take a close note at the code for the CorrelationID property. This property is a 20-element array of bytes. You will need to first convert it to a string and then run it through Visual FoxPro’s CreateBinary function.

    Advanced VFP Programming for MSMQ

    In this section, we will explore a number of advanced topics that will be of value in your MSMQ application.

    Sending Private Messages

    MSMQ provides a secured channel for sending private, encrypted messages throughout your MSMQ enterprise. MSMQ ensures that the body of private messages are kept encrypted from the moment they leave the source Queue Manager to the moment they reach their target Queue Manager.

    With encryption and decryption provided by MSMQ Queue Managers, applications do not have to encrypt messages when they are sent, or decrypt received messages. When a private message is sent, the source Queue Manager encrypts the body of the message, then sends the message on to the target Queue Manager. When the target Queue Manager receives the message, it decodes the body of the message and passes the clear message on to the queue. The receiving application can then read the message from the queue without ever knowing it was encrypted.

    Note   The receiving application sees the message as clear text. However, it can look at the message’s privacy level to determine whether the message was sent encrypted, or look at the encryption algorithm used when the message was sent.

    Sending private messages is easy:

    1. Optional. Verify that the queue can receive private messages. The MSMQQueueInfo object’s PrivLevel must be set to MQ_PRIV_LEVEL_BODY or MQ_PRIV_LEVEL_OPTIONAL. If set to MQ_PRIV_LEVEL_BODY, the queue can only accept private messages. Non-private messages will be ignored.oQueueInfo.PrivLevel = MQ_PRIV_LEVEL_BODY
    2. Open the queue for sending messages.
    3. Set the MSMQMessage object’s PrivLevel property to MQMSG_PRIV_LEVEL_BODY.oMsg.PrivLevel = MQMSG_PRIV_LEVEL_BODY
    4. Optional. Set the encryption algorithm used to encrypt the message.oMsg.EncryptAlgorithm = MQMSG_CALG_RC4
    5. Send the message.

    Here is a sample VFP snippet putting it all together:

    * Create the queue with Privacy setting
    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.pathname = “.\”+lcQueueName  &&must be unique
    oQueueInfo.Label = lcQueueName
    oQueueInfo.PrivLevel = MQ_PRIV_LEVEL_OPTIONAL
    oQueueInfo.Journal = 1  
    oQueueInfo.Create
    
    * Create new message
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    oMsg  = create(“msmq.msmqmessage”)
    oMsg.Label = “My Private Message”
    oMsg.Body = “This is a private message.”
    oMsg.PrivLevel = MQMSG_PRIV_LEVEL_BODY
    oMsg.EncryptAlgorithm = MQMSG_CALG_RC4
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    
    * Read the message
    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    oMsg = oRecQueue.ReceiveCurrent(,,,100)
    If oMsg.PrivLevel = MQMSG_PRIV_LEVEL_BODY
       ?”Private message: “ + oMsg.Label
    ELSE
       ?”Public message: “ + oMsg.Label
    ENDIF
    ?  + oMsg.Body
    ?  “Encryption alogithm is: “ + TRANS(oMsg.EncryptAlgorithm)
    oMsg = oRecQueue.ReceiveCurrent(,,,100)
    oRecQueue.Close
    

    Message Delivery Options

    An important concept you should understand when sending messages is the various Delivery options.

    • MQMSG_DELIVERY_RECOVERABLE – In every hop along its route, the message is forwarded to the next hop or stored locally in a backup file until delivered. This guarantees delivery even in the case of a machine crash.
    • MQMSG_DELIVERY_EXPRESS – The default. The message stays in memory until it can be delivered. (In-memory message store and forward.)

    When the message’s delivery mechanism is set to MQMSG_DELIVERY_EXPRESS, the message has faster throughput. When set to MQMSG_DELIVERY_RECOVERABLE, throughput may be slower. However, MSMQ guarantees that the message will be delivered, even if a computer crashes while the message is en-route to the queue.

    Handling this is in your code is simply a matter of setting a single message property.

    oMsg.Delivery = MQMSG_DELIVERY_EXPRESS 
    oMsg.Send(oSendQueue)
    

    Note   Messages must be sent in recoverable mode if the offline client computer is turned off. Messages sent in express mode are held in RAM and will be lost when the computer is turned off.

    Binding Excel Objects to Message Bodies

    One of the truly powerful features of MSMQ is the ability to attach COM objects to the bodies of messages. This works for quite a few common objects, including ADO recordsets and Excel spreadsheets. In this section, we will look at spreadsheets. Before you think about trying to attach a Visual FoxPro object, be aware of the following rule for message body content.

    The body of an MSMQ message can be a string, an array of bytes, or any persistent COM object that supports IDispatch and IPersist (IPersistStream or IPersistStorage).

    Note   Visual FoxPro 6.0 objects do not support the ability to persist themselves via IPersist.

    There are several ways to attach Excel data to your message. The following example demonstrates creating a message and attaching an existing Excel file to the message. You can use Visual FoxPro’s GETOBJECT() function to get an object reference to the file and set it directly to the Body property.

    * Attach existing Excel worksheet to message body
    #DEFINE cXLFile  “c:\offices.xls”
    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “Excel OFFICES.XLS Message”
    oMsg.Body = Getobject(cXLFile)
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    Alternatively, you can create an Excel spreadsheet object on the fly and persist its contents to the message body.

    * Attach Excel worksheet to message body
    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “New Excel Object Message”
    oXL=Create(“excel.application”)
    oXL.workbooks.add
    oXL.rows(1).columns(1).value = “Dogs”
    oXL.rows(1).columns(2).value = “Cats”
    oXL.rows(2).columns(1).value = 333
    oXL.rows(2).columns(2).value = 555
    oMsg.Body = oXL.activesheet.parent
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    oXL.DisplayAlerts = .F.
    oXL.quit
    

    Before reading the contents of your Excel spreadsheet object, first check the type of object you are dealing with. The following code uses the TYPE() function to check the object type, and shows accessing the Active Sheet object reference to obtain specific spreadsheet content. There is also code to iterate through the contents of the spreadsheet, however, you can use whatever automation calls you prefer.

    Note   The message body contains a reference to the application object.

    * Read in the Excel object
    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    oMsg = oRecQueue.PeekCurrent(,,100)
    oObj = oMsg.Body
    DO WHILE !ISNULL(oObj)
      IF TYPE(“oObj.Application.Name”)=“C” AND;
        ATC(“Excel”,oObj.Application.Name)#0
        oMsg = oRecQueue.ReceiveCurrent(,,,100)
        oObj = oMsg.Body
        oSheet = oObj.ActiveSheet
        ? “Excel contents:”
        FOR i = 1 TO 100
       IF EMPTY(oSheet.Rows(m.i).Columns(1).Text)
          EXIT
       ENDIF
          ?
       FOR j = 1 TO 100
          lcText = oSheet.Rows(m.i).Columns(m.j).Text
          IF EMPTY(lcText)
             EXIT
          ENDIF
             ?? lcText+”,”
       ENDFOR
        ENDFOR
        EXIT
      ENDIF
      oMsg = oRecQueue.PeekNext(,,100)
      oObj = oMsg.Body
    ENDDO
    oRecQueue.Close
    

    Binding ADO Recordset Objects to Message Bodies

    ADO recordsets represent a valuable medium for passing data via MSMQ messages. Rather than passing a long string representing a record from your FoxPro table, which you would later need to parse, you can pass a more efficient object. Additionally, the data is stored in an encrypted format.

    Using the new VFPCOM utility available from the Visual FoxPro Web site, (https://msdn.microsoft.com/vfoxpro/downloads/updates.asp), you now have an excellent mechanism for passing Visual FoxPro data. VFPCOM contains methods for converting VFP data to/from ADO recordsets. The following sample shows VFPCOM used to convert a VFP cursor to a recordset, and then attach that recordset to a message.

    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “ADO Message”
    USE _samples+”\data\employee” AGAIN SHARED
    oComUtil = create(“vfpcom.comutil”)
    oRS = CreateObject(“ADODB.Recordset”)
    oComUtil.CursorToRS(oRS)
    oMsg.Body = oRS
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    Reading and detaching the ADO recordset is easy as demonstrated in the following code, but don’t forget to first check for a valid ADO object. Use the TYPE() function with a common known member of the object.

    * Receive the messages
    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
    * Get the ADO object
    oMsg = oRecQueue.PeekCurrent(,,100)
    oObj = oMsg.Body
    DO WHILE !ISNULL(oObj)
       IF TYPE(“oObj.eof”)=“L”
          oMsg = oRecQueue.ReceiveCurrent(,,,100)
          oObj = oMsg.Body
          oObj.MoveFirst
          DO WHILE !oObj.eof
             ? oObj.Fields[1].Value
             oObj.MoveNext
          ENDDO
          EXIT
       ENDIF
       oMsg = oRecQueue.PeekNext(,,100)
       oObj = oMsg.Body
    ENDDO
    oRecQueue.Close
    

    Using XML with Messages

    One of the more exciting uses for messages is passing of XML data. XML data can be used for a variety of purposes, including storage of relational data and other structured information. MSMQ messages are nicely suited for passing XML data, because the message body can persist this structured data.

    We’ve already discussed passing ADO recordsets and Microsoft Office objects such as Excel spreadsheets. You’re probably wishing that it was easy to attach VFP cursors and objects to messages as well. XML is a wonderful medium for doing just this.

    Rick Strahl’s Web site (www.west-wind.com/) contains an excellent set of XML classes called wwXML, which you can use with your applications. The following table contains some of the wwXML methods of interest.Expand table

    MethodDescription
    CursorToXMLConverts a cursor into an XML representation.
    XMLToCursorConverts an XML document created with CursorToXML back into a cursor.
    ObjectToXMLConverts a live reference of an object to XML. All variables are converted to text and stored. Optionally can walk nested objects.
    XMLToObjectCreates an object from an XML structure.

    Here is an example using wwXML to persist a VFP cursor.

    #DEFINE WWXML_PATH   “C:\VFP\WWXML\”
    LOCAL lcQueueName,oQueueInfo,oSendQueue, oXML,oMSG
    SET CLASSLIB TO (WWXML_PATH + “wwXML.vcx”) ADDITIVE
    SET PROCEDURE TO (WWXML_PATH + “wwUtils.prg”) ADDITIVE
    
    lcQueueName = “myXMLqueue”
    oQueueInfo = CreateObject(“msmq.msmqqueueinfo”)
    oQueueInfo.Formatname = “DIRECT=OS:.\”+lcQueueName
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    
    USE _samples+”\data\customer” AGAIN SHARED
    oXML = NewObject(“wwXML”)
    oXML.lCreateDataStructure = .T.
    
    oMsg = create(“msmq.msmqmessage”)
    oMsg.Label = “Persisted VFP Cursor - “ + TRANS(DATETIME())
    oMsg.Body = oXML.CursorToXML()
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    One of the considerations when using XML with MSMQ 1.0, is that the XML will be stored as simple text, which typically is not encrypted. You may need to take some precautions and use security. There is also an EncryptAlgorithm message property that can be used to encrypt the message, however, this only applies for private messages.

    Working with MSMQ Events and Asynchronous Message Reading

    Applications such as Visual Basic® allow you to bind to events of COM objects. Visual FoxPro 6.0 supports this with ActiveX controls, but not with COM objects such as an ADO Recordset. This means you will not have the ability to bind a VFP object to a COM object and have user code executed when an event of the COM object is triggered.

    The new VFPCOM utility (see above) addresses this critical need. You can now have VFP code executed when a message arrives in a particular queue. MSMQ triggers an event that VFP can respond to. The advantage here is that your application can now handle reading messages in the queue asynchronously. Instead of having some object using up lots of processor time consumed in an endless DO WHILE loop, you can have your dormant objects awaken only when a message arrival event occurs.

    The process of setting up a queue for asynchronous reading of messages is as follows:

    1. Create your VFP MSMQ events class to handle reading of messages as they arrive in queues. This class needs to contain Arrived and ArrivedError methods to handle the message arrival events.
    2. Use VFPCOM to bind an instance of your VFP MSMQ events class to an instance of an MSMQEvent object. This is done with VFPCOM’s BindEvents method.
    3. Open the queue and call its EnableNotification method passing the MSMQEvent object you just setup with BindEvents.

    Note   You must leave the queue open and the BindEvents call intact, otherwise your event connection loses scope and will fail.

    The following class definition is a sample template that can be used for your event binding. Notice that you can use the AppSpecific property to special case handling for certain messages.

    DEFINE CLASS foxevent AS custom
       Procedure Arrived(oQueue,Cursor)
            oMsgRec = oQueue.PeekCurrent(,,0)
       ? “Message Arrived Event: “+TRANS(oMsgRec.AppSpecific)
       IF oMsgRec.AppSpecific = 33  &&get only this one
          oMsgRec = oQueue.ReceiveCurrent(,,,0)
          oMsgRec = oQueue.PeekCurrent(,,0)
          oQueue.EnableNotification(oMSMQEvent,MQMSG_CURRENT,1000)   
       ELSE
          oQueue.EnableNotification(oMSMQEvent,MQMSG_NEXT,1000)   
       ENDIF
       ENDPROC
    
       Procedure ArrivedError(Queue,ErrorCode,Cursor)
       ? “Message Arrived Error”
       ENDPROC
    ENDDEFINE
    

    Once you have your class defined, you can set things up for asynchronous reading of messages. The remainder of the code below shows how to set up the event handler. This is also a simple way to test the event handling by adding a bunch of messages.

    * Setup up event handler to receive the messages
    oMSMQEvent = create(“msmq.msmqevent”)
    oComUtil = create(“vfpcom.comutil”)
    oFoxEvents = create(“foxevent”)
    oComUtil.BindEvents(oMSMQEvent,oFoxEvents)
    oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS,MQ_DENY_NONE)
    oRecQueue.EnableNotification(oMSMQEvent,MQMSG_CURRENT,1000)
    
    * Add a bunch of new message
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    oMsg = create(“msmq.msmqmessage”)
    oMsg.AppSpecific = 11
    oMsg.Send(oSendQueue)
    oMsg.AppSpecific = 22
    oMsg.Send(oSendQueue)
    oMsg.AppSpecific = 33
    oMsg.Send(oSendQueue)
    oMsg.AppSpecific = 44
    oMsg.Send(oSendQueue)
    oMsg.AppSpecific = 55
    oMsg.Label = “Message55”
    oMsg.Body = “This is body of message 55.”
    oMsg.Send(oSendQueue)
    oSendQueue.Close
    

    Working with MSMQ Transactions

    MSMQ transactions are used by the sending and receiving application. In this model, MSMQ uses two transactions, one to send messages to the queue and the other to retrieve messages from the queue. The sending transaction can commit to sending the messages to the queue and the receiving application can commit to retrieving the messages; MSMQ provides its own confirmation process to notify the sending application that either the messages were retrieved from the queue or why the receiving application failed to retrieve them. MSMQ provides both implicit and explicit mechanisms for incorporating transactions. There are two types of implicit transactions, MTS Transactions and XA-Compliant Transactions. Let’s quickly look at the two explicit ones (MSMQ Internal Transactions and MS DTC External Transactions) and then come back to the MTS Transactions.

    MSMQ Internal Transactions

    MSMQ Internal Transactions provide better performance for transactions that only send or receive MSMQ messages. Unlike MS DTC external transactions, MSMQ internal transactions cannot be passed to another resource manager. It is the cost of coordinating between several resource managers that make MSMQ internal transaction less expensive in terms of memory than MS DTC external transactions.

    Note   When sending a single message, MSMQ provides a single-message send operation that uses an MSMQ internal transaction. This mode of sending a message provides the best performance of all transaction types. When using this mode, MQBeginTransaction and Commit are implied.

    When setting up your queue for handling internal transactions, you must ensure that the queue is created to allow for transactions. This is handled in the Create() method shown below.

    oQueueInfo = create(“msmq.msmqqueueinfo”)
    oQueueInfo.pathname = “.\”+lcQueueName
    oQueueInfo.Label = lcQueueName
    oQueueInfo.Journal = 1
    * First parameter determines if queue is transactional
    oQueueInfo.Create(.T.)
    

    Sending Messages with Transactions

    Transactions can be included at the sending and receiving end of messages. The process for setting up messages for transactions is quite simple. You create an object instance of the MSMQ Transaction Dispenser object, to start a transaction. This object has a single method called BeginTransaction, which returns an MSMQ Transaction object. This object is then used to control Commit/Abort operations on sending/receiving messages. The following code demonstrates how this works for sending messages.

    * Setup Transaction and Transaction Dispenser objects
    oTransDisp  = create(“msmq.msmqtransactiondispenser”)
    oTrans = oTransDisp.BeginTransaction
    
    * Create new message
    oMsg  = create(“msmq.msmqmessage”)
    oMsg.Label = “My Trans Message”
    oMsg.Body = “This is a trans message.”
    oMsg.Send(oSendQueue, oTrans)
    oSendQueue.Close
    

    If your queue is not set up to handle transactions, you will get an error during the Send method call.

    At this point, we have a message that we sent to our queue. The message does not appear in the queue in the MSMQ Explorer because the Send transaction was not yet committed. If you commit the operation by calling the Transaction object’s commit method, then the message is posted to the queue. If you abort, then it is discarded.

    IF MESSAGEBOX(“Do you want to commit sending message(Yes) ;
      or abort(No)?”,36)=6
        oTrans.Commit
    ELSE
        oTrans.Abort  && causes message to be disgarded
        MESSAGEBOX(“Queue should be empty since message was disgarded.”)
        RETURN
    ENDIF
    

    Note   In the example I was actually able to commit/abort the transaction even after I closed the queue.

    Receiving Messages with Transactions

    A transaction can be associated with receiving message, just as it was with sending messages.

    oTransDisp  = create(“msmq.msmqtransactiondispenser”)
    oTrans = oTransDisp.BeginTransaction
    oMsg = oRecQueue.ReceiveCurrent(oTrans,,,100)
    ? oMsg.Label
    ? oMsg.Body
    oRecQueue.Close
    
    IF MESSAGEBOX(“Do you want to commit receiving message(Yes) ;
      or abort(No)?”,36)=6
        oTrans.Commit
        MESSAGEBOX(“Queue should be empty with message moved to Journal.”)
    ELSE
        oTrans.Abort 
        MESSAGEBOX(“Queue should contain unread message.”)
    ENDIF
    

    The Abort operation does not actually discard the message. It instead treats it as unread.

    Note   When message are not processed, this can be used as an alternate approach to using the Peek functions to read messages.

    MS DTC External Transactions

    MS DTC External Transactions are used when the transaction includes more actions than simply sending or retrieving MSMQ messages (more than one resource manager is used). In this case, the application must ask MS DTC (Microsoft® Distributed Transaction Coordinator) for a transaction object and explicitly reference that object each time it sends a message, retrieves a message, or executes an action of another resource manager.

    When an application is performing a MS DTC transaction, MSMQ is acting as part of a transaction processing system that includes a transaction manager and any number of resource managers.

    Programming to use explicit external transactions is not much different from the examples shown above with internal transactions. In fact, the only difference is the Transaction Dispenser being used.

    oTransDisp  = create(“msmq.msmqCoordinatedTransactionDispenser”)
    

    If you get an error message after your BeginTransaction call, you probably don’t have DTC running. You can turn it on via the SQL Server Service Manager (note: many people set their system to automatically turn on DTC during startup).

    oTransDisp  = create(“msmq.msmqCoordinatedTransactionDispenser”)
    oTrans = oTransDisp.BeginTransaction
    

    One interesting difference between the DTC External and Internal Transactions is that when you abort Receiving a message with a DTC External Transaction, the message is left intact in the queue. With Internal Transactions, the message is purged and moved to the Xact Dead Letter queue.

    MTS Transactions

    One of the nice features of MSMQ is its implicit coordination with Microsoft® Transaction Server to handle transactions. As you may know, MTS is a runtime environment used to handle robust distributed transactions. Visual FoxPro works very well with MTS, and with Visual FoxPro 6.0 SP3, you can achieve highly scalable applications using new multi-threaded DLL servers. When the application is running in the MTS environment, MSMQ can use the current MTS transaction if one is available.

    Programming for MTS Transactions requires using an MTS context object to obtain and make transactional calls. Again, you can control transactions in MSMQ for both sending and receiving messages. Unlike the explicit types shown above, the Send and Receive calls do not take a Transaction object as parameter, but rather a constant (MQ_MTS_TRANSACTION).

    oMsg.Send(oSendQueue, MQ_MTS_TRANSACTION)
    oMsg = oRecQueue.ReceiveCurrent(MQ_MTS_TRANSACTION,,,100)
    

    In your MTS server component, you can control Commit/Abort operations by using the SetAbort and SetComplete methods of the MTS context object. Below is pseudo-sample of how your MTS server might be structured to handle an MSMQ message in a transaction.

    * Your MTS server
    oMTX = Create(“MTXAS.APPSERVER.1”)
    oContext = oMTX.GetObjectContext()
    
    * Create new message and send it to queue
    oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
    oMsg  = create(“msmq.msmqmessage”)
    oMsg.Label = “My MTS Message”
    oMsg.Body = “This is a MTS message.”
    oMsg.Send(oSendQueue, MQ_MTS_TRANSACTION)
    oSendQueue.Close
    
    * Do some other stuff here which may not cause abort action
    IF lHadSomeError
      oContext.SetAbort
    ELSE
      oContext.SetComplete
    ENDIF
    

    Transaction Programming Considerations

    • If some operations in a transaction fail, it is the application’s responsibility to decide whether to terminate the entire transaction (by calling the Transaction object’s abort member function) or commit the transaction anyway (if the failures are such that the transaction is still viable). If the application does commit to a transaction where some operations have failed, the failed operations will not be part of the transaction.
    • There is no limit to the number of messages sent, the number of messages retrieved, or the number of queues used in a single transaction. However, an application cannot send a message to a queue and then try to retrieve it during the same transaction.
    • Calling MQSendMessage does not actually send the message within the transaction. The actual sending is done at some time after MS DTC commits the transaction. When MS DTC returns a successful commit return value, the sending application is guaranteed that the message will be sent. If a transaction is aborted, all MSMQ transaction operations are rolled back: no messages are sent, and all retrieved messages are returned to their original place in the queue.
    • MSMQ guarantees exactly-once-delivery. This means that all messages sent to a queue will arrive once and only once. MSMQ takes special measures to prevent any message duplication or loss.
    • MSMQ guarantees that all messages sent to a specific transaction queue will arrive in the order they were sent by the transaction. This means that if transaction T1 sends messages M1 and M2 to queue Q1, M1 will arrive before M2. However, there is no guarantee if two transactions are sending messages to the same queue. If transaction T1 sends messages M1 and M2 to Q1, and a second transaction T2 sends messages M3 and M4 to Q1, MSMQ only guarantees that M1 will arrive before M2, and that M3 will arrive before M4. In order to guarantee that M1 and M2 will arrive before M3 and M4, the application must commit to T2 only after getting a successful return code from T1.MSMQ does not guarantee order of delivery to different queues, nor does it guarantee order of delivery from different computers.

    Message Authentication

    Message authentication allows the receiving application to verify the source of a message and that the message was not modified on its way to the queue. This is done by attaching a digital signature to the message when it is sent, then verifying the digital signature when the message reaches the queue. The receiving MSMQ Queue Manager uses the digital signature to verify the sender and that the message was not modified.

    To digitally sign a message, the sending application uses a public and private signing key pair to create the digital signature. MSMQ provides the key pair when an internal security certificate is used or when an external security certificate is used. External certificates are obtained from a certificate authority (CA).

    When an internal security certificate is used, the private signing key is registered the first time that the MSMQ Control Panel application is run. The public signing key is provided within the internal certificate.

    Internal certificates are used when the receiving application needs to validate the sender identifier attached to a message. When using an internal certificate, only the sender identifier is guaranteed correct.

    External certificates are used when you want to use the information in the certificate (not just the sender identifier sent with the message) to verify the source of a message. The information in the external certificate is guaranteed by the certificate authority that created the certificate.

    MSMQ does not validate an external certificate. The receiving application must validate the certificate before using an authenticated message. MSMQ generates the digital signature of a message when it is sent and verifies the digital signature when the message is received, but does not validate the certificate itself.

    Note   External certificates are required when communicating with operating environments other than Windows NT® where the sender identifier is meaningless.

    Going Forward

    As you no doubt have seen throughout the article, MSMQ offers endless opportunities for Visual FoxPro developers. MSMQ is a wonderful technology for use with Windows DNA applications where Visual FoxPro COM components can play an essential role in the middle tier.

    The samples included with this article contain valuable code you can use in your own applications. You might consider creating reusable classes for common operations such as opening queues, sending and receiving messages. Additionally, it is also useful to have a handy set of tools for working with MSMQ. The following tools included with the samples are invaluable in helping to develop and debug your MSMQ applications:

    MSGDISPATCH.SCX – lets you send a message to a queue. The form allows you to send normal text messages, but also lets you drag and drop Office documents (e.g., DOC, XLS and PPT files) onto the body area to have them attached to the message. Messages can be sent to both private and public queues. An admin queue can also be set up for message acknowledgments.

    MSGREADER.SCX – while the MSMQ Explorer does not allow you to send messages to a queue, you can view them. Unfortunately, the body contents displayed in the dialog is not very usable, especially with non-text bodies. This form lets you view all sorts of message bodies including plain text, Office documents, ADO recordsets and XML from Rick Strahl’s wwwXML classes.

    EVENTHANDLER.SCX – asynchronous events . The EventHandler uses the new VFPCOM utility to bind a Visual FoxPro object, which implements the same interface as the MSMQEvent class, to an MSMQ queue so that you can have Fox code executed when a message arrives in that queue.

    All of the tools and samples are “smart” in detecting whether you are online or offline. If you are offline, then the tools will display Private queues to select.

    Finally, more information on using MSMQ can be found at the following:

    • Microsoft MSMQ Web site: www.microsoft.com/ntserver/appservice/exec/overview/MSMQ_Overview.asp – contains the best reference set for using MSMQ including samples, technical papers, and other available references.
    • MSDN: https://msdn.microsoft.com/default.asp – lots of great articles on using MSMQ.
    • Microsoft Systems Journal: www.microsoft.com/msj/default.asp – a number of useful MSMQ articles have appeared in MSJ over the past few years. Many of the samples are written in Visual Basic, which can easily be converted to Visual FoxPro syntax.
    • Books – although slow in release, good books are starting to appear on the shelves of your local bookstore. You can search on some of the online book web sites for keywords including MSMQ, MTS, and Distributed Applications.

    The Visual FoxPro 6.0 Class Browser 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. What Is the Visual FoxPro Class Browser?
    3. What’s New in Visual FoxPro 6.0
    4. The Browser Interface

    Show 4 more

    Steven M. Black

    September 1998

    Summary: Describes the Microsoft® Visual FoxPro® version 6.0 Class Browser and how, combined with the Component Gallery, it can be an effective tool for a variety of development purposes. (9 printed pages)

    Contents

    Introduction What Is the Visual FoxPro Class Browser? What’s New in Visual FoxPro 6.0 The Browser Interface Class Management Write Your Own Add-Ins Appendix: Browser.dbf Structure About the Author

    Introduction

    This article discusses the Visual FoxPro Class Browser. Together with the new Component Gallery, the Class Browser provides many useful ways to accomplish common development tasks. This document also discusses the open architecture and programmable hooks that these tools expose for customization purposes.

    What Is the Visual FoxPro Class Browser?

    The Class Browser is useful for a variety of development purposes, such as:

    • Managing classes and class libraries, including adding, deleting, renaming, and redefining classes.
    • Browsing all the classes and class libraries used by a project or application.
    • Generating equivalent code for visual classes.
    • Browsing the interfaces of Microsoft ActiveX® controls and Component Object Model (COM) components.
    • Creating running instances at design time.

    What’s New in Visual FoxPro 6.0

    Most changes to the Visual FoxPro 6.0 Class Browser enhance its usability.

    • Mode button (cmdBrowser) has been added to the toolbar to toggle between the Class Browser and the new Component Gallery.
    • The Type drop-down combo box is now on the same line as the toolbar, resulting in more efficient use of display real estate.
    • You can size the individual panes in the Class Browser because the boundaries between panes now behave like splitter controls.
    • The Class Browser Shortcut menu provides new options. You can now toggle the visibility of the Parent Class toolbar, open a new instance of the Class Browser, open a new instance of the Gallery, and force the Class Browser display to refresh.
    • In the methods and properties pane you’ll notice the methods and properties now display icons to show the status (public, protected, and hidden) of these members. These icons are consistent with those used in Visual Modeler.
    • The Redefine dialog box has been enhanced to use a conventional class specification dialog box, rather than requiring you to type the class name, and optionally use GetFile( ) to specify its .vcx file.
    • If you open .tlb, .dll, .olb, or .ocx files in the Class Browser, you’ll notice the typelib information now includes much more detail, including properties, methods, and their parameters.Note    Because much useful information appears in the lower-right description pane, make sure you use the Class Browser Shortcut menu to toggle the description panes on.
    • If the .tlb, .dll, .olb, or .ocx file displayed in the Class Browser has an associated Help file, pressing the space bar will bring up help for the highlighted item.
    • In the Class Browser, you can export code to HTML. With a class highlighted in the left pane, right-click the View Code button. This invokes your Web browser and opens the browser window over the right pane of the Class Browser.
    • The View Code window is now modeless.
    • Right-click the Gallery/Browser Mode button to see a history of previously selected items and files.
    • With a class selected, Ctrl+ right-click the Class icon image to generate a new object instance created in the Command window.
    • Users of low-resolution displays will note that you can now resize the Class Browser to a smaller size than was possible before. The browser’s minimum dimensions in Visual FoxPro 5.0 are 123×252 pixels with two lines of display, and in Visual FoxPro 6.0 they are 131×155 pixels with five lines of display, so the browser in Visual FoxPro 6.0 can display a third less real estate in tight situations.

    Of course, the big change in the Class Browser is integration with the new Component Gallery.

    The Browser Interface

    Figure 1. The Class Browser interface

    Here are a few things you should know about the Class Browser to increase your productivity with this tool.Expand table

    Click this button to see the class code. If the output contains nested classes, the code won’t execute. Right-click this button and see the class code in HTML format. The window that appears is actually an instance of your browser, wherein you can use the Shortcut menu to view or print the HTML for displaying the source.
    Use this button to create a new class. This new class can be a subclass of the currently selected class, a subclass of any other class, or a subclass of a Visual FoxPro base class.
    You can redefine classes in the Class Browser with this icon. In Visual FoxPro 6.0, you can redefine a class to that of a different base class. You are warned that you will lose some intrinsic methods and properties in the process.
    When you open a Method Code window, this button (which floats in its own toolbar independent of the Class Browser) allows you to view the code hierarchy in parent class methods.

    View more than class libraries

    In the Class Browser Open dialog box, note the different types of files that are supported. Figure 2 shows the Open dialog box pointing to the Component Gallery directory, with the drop-down list expanded to show the types of files you can show in the Class Browser.

    Figure 2. The Class Browser Open dialog box

    You can open Visual FoxPro forms in the Class Browser, and many of the features, such as code generation, work as you might expect. This is very handy for sharing your code with other users via e-mail.

    If you open a Visual FoxPro project file, all the class libraries in that project will be visible in a single view. This is great if your project contains hundreds of classes and you have no idea in which library a particular class belongs.

    In the Class Browser you can open any .exe or COM files (for example, .ocx, .dll, or .tlb) and display their public interfaces. If Help is available, pressing the spacebar invokes it.

    To add controls to a form or class

    In the Class Browser, open the .vcx (class library) file containing the class of the object you want to add to the form, select the class, and then use the drag-and-drop operation to move the Class icon to the design surface.

    Invoke the class of a selected item on a form

    In the form or Class Designer, select a control. If you then invoke the Class Browser, the class and its class library will be displayed with the class for the selected control highlighted. This works with running forms too, except that it uses the control that has focus.

    Class Management

    Bewildered by many of the class management activities that you can do with the Class Browser? Then see the article, “Managing Classes with Visual FoxPro.”

    Write Your Own Add-Ins

    Class Browser add-ins are a built-in way you can extend the Class Browser. Add-ins are useful because of the endless variety of things you can do with them. For example, you can write an add-in to recursively call the Class Browser’s ExportCode( ) method to generate all the code for a particular class library. You can also create an add-in to modify how ExportCode( ) works, like run beautify.app as a post process to format the exported code just as you please.

    All Class Browser events and methods contain hooks to accept add-ins, and add-ins can also be run independently of Class Browser events and methods. See “Creating Add-Ins for the Visual FoxPro Class Browser” for more information.

    There you have it: a quick cafeteria-style tour of some of the neat things in the Visual FoxPro 6.0 Class Browser. Of course, you should also read the Visual FoxPro online Help for more details. These are great tools, and if you use Visual FoxPro every day it’s likely that you could make much better use of them.

    Appendix: Browser.dbf Structure

    The Class Browser stores all its metadata in a table named Browser.dbf found in your HOME() directory. The Component Gallery also uses Browser.dbf to store its catalog-related information. Table 1 provides a field-by-field description of important elements in Browser.dbf.

    Table 1. Browser.dbf MetadataExpand table

    FieldValuesDescriptionBG
    PLATFORMWINDOWS
    <EMPTY>
    Applies to records NOT of TYPE=”ADDIN.”
    Applies only to records of TYPE=”ADDIN.”
    TYPEPREFW

    ADDIN
    Specifies that the record stores browser and gallery preferences.
    Specifies that the record stores add-in information.
    IDFORMINFO


    BROWSER


    METHOD


    MENU
    Specifies that a Class Browser record stores form preferences; specifies that Gallery record stores catalog information.
    Specifies that record contains default settings for the Class Browser. See the PROPERTIES field.
    Specifies that the record contains a Class Browser add-in that is tied to a particular event or method.
    Specifies that the record stores a Class Browser add-in that is NOT tied to a particular event or method, and is thus available on the Add-in menu.
    • • • •• • •
    DEFAULT.T.
    .F.
    Specifies a Component Gallery catalog.
    Specifies a Class Browser record. The default is false (.F.).
     
    GLOBAL.T.
    .F.
    Specifies that a gallery catalog is global. The default is false (.F.) 
    BACKUP.F.
    .T.
    Specifies whether a backup is attempted the next time the file is opened. The default is false (.F.).When you open a catalog or a .vcx file in the Class Browser or Component Gallery, this field in the associated browser.dbf record is queried. If the value is logical .T. (true), a search is made for a file of the same name in the backup subfolder. If the backup file doesn’t exist, one is automatically created in a subfolder called Backup. Then the BACKUP field is set to .F.Through add-in hooks or with any program that opens and updates browser.dbf, you can set this field to force the Class Browser or Component Gallery to automatically back up a file or table the next time that file is opened, and only the next time.This feature is used internally in one special case; when browser.dbf is first created after Visual FoxPro is installed, a new browser.dbf is created with the default catalogs (around 5 or so). The BACKUP field is set to .T. so that each catalog gets backed up the first time it is opened because Visual FoxPro does not install the associated backup catalog tables. Beyond that special function, it can be used at will by developers for their own purpose.
    NAMEcFilenameA memo field that specifies the file name that relates to the current record. This value appears in the add-ins Shortcut menu if the add-in is NOT tied to an event or method.In Class Browser records the file extension can be .vcx, .pjx, .scx, .ovx, .dll, .exe, .app, or others. In Component Gallery records the file extension is .dbf.
    DESCcDescriptionA memo field that contains the description of the catalog referred to in the NAME field. 
    METHODcMethodNameA memo field that specifies the method to which a Class Browser or Component Gallery add-in is tied. If the method field is equal to “*” the add-in will execute for all methods.
    PROPERTIESmemoA memo field that specifies the default settings. 
    SCRIPT Internal Gallery use only. 
    PROGRAMcPRGFilenameA memo field that contains the name of the program to be run by a .prg-based add-in.
    CLASSLIBcClasslibNameA memo field that contains the name of the class library to be used by a .vcx-based add-in.
    CLASSNAMEcClassNameA memo field that contains the name of the class to be used by a .vcx-based add-in.
    DISPMODE<n>Specifies the display mode of the class library.
    1 = hierarchical
    2 = alphabetic
     
    TOP<nnn>A numeric field that specifies the stored top (y) coordinate for the Class Browser/Component Gallery form.
    LEFT<nnn>A numeric field that specifies the stored left (x) coordinate for the Class Browser/Component Gallery form.
    HEIGHT<nnn>A numeric field that specifies the stored height of the Class Browser/Component Gallery form.
    WIDTH<nnn>A numeric field that specifies the stored width of the Class Browser/Component Gallery form.
    HEIGHT1<nnn>A numeric field that specifies the stored height of the class and member description panes in the Class Browser. 
    HEIGHT2<nnn>A numeric field that specifies the stored height of the item description pane in the Component Gallery. 
    WINDOWSTAT<n>A numeric field that specifies the characteristics of the Class Browser or Component Gallery window.
    0 = Normal window
    1 = Minimized window
    2 = Maximized window
    PROTECTED.F.
    .T.
    A logical field that specifies whether protected members are displayed. The default is false (.F.). 
    EMPTY.F.
    .T.
    A logical field that specifies whether empty methods are to be displayed. 
    HIDDEN.F.
    .T.
    A logical field that specifies whether hidden members are to be displayed. 
    DESCBOXES.F.
    .T.
    A logical field that specifies whether description panes are to be displayed. 
    AUTOEXPAND.F.
    .T.
    A logical field that specifies whether hierarchical items in the treeview are to be automatically displayed expanded in the left-hand side pane. 
    PUSHPIN.F.
    .T.
    A logical field that specifies whether the display is always on top. 
    PCBROWSER.F.
    .T.
    A parent class toolbar flag. A logical field that specifies whether the toolbar is on for that file.  
    VIEWMODE<n>A numeric field that specifies the mode of the Gallery listview.
    1. Large (standard) Icons
    2. Small Icons
    3. List
    4. Report
     
    FONTINFOcFontPrefA memo field that contains the current display font preference.
    FORMCOUNT<n>A numeric field that specifies the number of Class Browser instances running for the .vcx file.
    UPDATED<DateTime>A datetime field that specifies when this record was last updated.
    COMMENT Unused
    User1….4 Unused

    About the Author

    Steven specializes in developing multilingual, multisite, and other challenging software situations, including project turnarounds and cleanups. He is the creator of Steven Black’s INTL Toolkit, a multilingual framework for FoxPro and Visual FoxPro. He’s a regular speaker at Visual FoxPro conferences, and his contributions occasionally darken the pages of FoxPro books and magazines.

    Supported Visual FoxPro SET Commands

    Unsupported Visual FoxPro Commands and Functions

    Symbols
    & Command

    && Command

    • Command

    NAME?

    ? | ?? Command

    ??? Command

    @ … BOX Command

    @ … CLASS Command

    @ … CLEAR Command

    @ … EDIT – Edit Boxes Command

    @ … FILL Command

    @ … GET – Check Boxes Command

    @ … GET – Combo Boxes Command

    @ … GET – Command Buttons Command

    @ … GET – List Boxes Command

    @ … GET – Option Buttons Command

    @ … GET – Spinners Command

    @ … GET – Text Boxes Command

    @ … GET – Transparent Buttons Command

    @ … MENU Command

    @ … PROMPT Command

    @ … SAY – Pictures & OLE Objects Command

    @ … SAY Command

    @ … SCROLL Command

    @ … TO Command

    \ | \ Command

    A
    ACCEPT Command

    ACTIVATE MENU Command

    ACTIVATE POPUP Command

    ACTIVATE SCREEN Command

    ACTIVATE WINDOW Command

    ADD CLASS Command

    ADD TABLE Command

    ALTER TABLE – SQL Command

    APPEND Command

    APPEND FROM ARRAY Command

    APPEND FROM Command

    APPEND GENERAL Command

    APPEND MEMO Command

    APPEND PROCEDURES Command

    ASSERT Command

    ASSIST Command

    AVERAGE Command

    B
    BEGIN TRANSACTION Command

    BLANK Command

    BROWSE Command

    BUILD APP Command

    BUILD DLL Command

    BUILD EXE Command

    BUILD MTDLL Command

    BUILD PROJECT Command

    C
    CALCULATE Command

    CALL Command

    CANCEL Command

    CD | CHDIR Command

    CHANGE Command

    CLEAR Commands

    CLOSE Commands

    CLOSE MEMO Command

    COMPILE Command

    COMPILE DATABASE Command

    COMPILE FORM Command

    CONTINUE Command

    COPY FILE Command

    COPY INDEXES Command

    COPY MEMO Command

    COPY PROCEDURES Command

    COPY STRUCTURE Command

    COPY STRUCTURE EXTENDED Command

    COPY TAG Command

    COPY TO ARRAY Command

    COPY TO Command

    COUNT Command

    CREATE CLASS Command

    CREATE CLASSLIB Command

    CREATE COLOR SET Command

    CREATE Command

    CREATE CONNECTION Command

    CREATE CURSOR – SQL Command

    CREATE DATABASE Command

    CREATE FORM Command

    CREATE FROM Command

    CREATE LABEL Command

    CREATE MENU Command

    CREATE PROJECT Command

    CREATE QUERY Command

    CREATE REPORT – Quick Report Command

    CREATE REPORT Command

    CREATE SCREEN – Quick Screen Command

    CREATE SCREEN Command

    CREATE SQL VIEW Command

    CREATE TABLE – SQL Command

    CREATE TRIGGER Command

    CREATE VIEW Command

    D
    DEACTIVATE MENU Command

    DEACTIVATE POPUP Command

    DEACTIVATE WINDOW Command

    DEBUG Command

    DEBUGOUT Command

    DECLARE – DLL Command

    DECLARE Command

    DEFINE BAR Command

    DEFINE BOX Command

    DEFINE CLASS Command

    DEFINE MENU Command

    DEFINE PAD Command

    DEFINE POPUP Command

    DEFINE WINDOW Command

    DELETE – SQL Command

    DELETE Command

    DELETE CONNECTION Command

    DELETE DATABASE Command

    DELETE FILE Command

    DELETE TAG Command

    DELETE TRIGGER Command

    DELETE VIEW Command

    DIMENSION Command

    DIR or DIRECTORY Command

    DISPLAY Command

    DISPLAY CONNECTIONS Command

    DISPLAY DATABASE Command

    DISPLAY DLLS Command

    DISPLAY FILES Command

    DISPLAY MEMORY Command

    DISPLAY OBJECTS Command

    DISPLAY PROCEDURES Command

    DISPLAY STATUS Command

    DISPLAY STRUCTURE Command

    DISPLAY TABLES Command

    DISPLAY VIEWS Command

    DO CASE … ENDCASE Command

    DO Command

    DO FORM Command

    DO WHILE … ENDDO Command

    DOEVENTS Command

    DROP TABLE Command

    DROP VIEW Command

    E
    EDIT Command

    EJECT Command

    EJECT PAGE Command

    END TRANSACTION Command

    ERASE Command

    ERROR Command

    EXIT Command

    EXPORT Command

    EXTERNAL Command

    F
    FIND Command

    FLUSH Command

    FOR EACH … ENDFOR Command

    FOR … ENDFOR Command

    FREE TABLE Command

    FUNCTION Command

    G
    GATHER Command

    GETEXPR Command

    GO | GOTO Command

    H
    HELP Command

    HIDE MENU Command

    HIDE POPUP Command

    HIDE WINDOW Command

    I
    IF … ENDIF Command

    IMPORT Command

    INDEX Command

    INPUT Command

    INSERT – SQL Command

    INSERT Command

    J
    JOIN Command

    K
    KEYBOARD Command

    L
    LABEL Command

    LIST Commands

    LIST CONNECTIONS Command

    LIST DATABASE Command

    LIST DLLS Command

    LIST OBJECTS Command

    LIST PROCEDURES Command

    LIST TABLES Command

    LIST VIEWS Command

    LOAD Command

    LOCAL Command

    LOCATE Command

    LOOP Command

    LPARAMETERS Command

    M
    MD | MKDIR Command

    MENU Command

    MENU TO Command

    MODIFY CLASS Command

    MODIFY COMMAND Command

    MODIFY CONNECTION Command

    MODIFY DATABASE Command

    MODIFY FILE Command

    MODIFY FORM Command

    MODIFY GENERAL Command

    MODIFY LABEL Command

    MODIFY MEMO Command

    MODIFY MENU Command

    MODIFY PROCEDURE Command

    MODIFY PROJECT Command

    MODIFY QUERY Command

    MODIFY REPORT Command

    MODIFY SCREEN Command

    MODIFY STRUCTURE Command

    MODIFY VIEW Command

    MODIFY WINDOW Command

    MOUSE Command

    MOVE POPUP Command

    MOVE WINDOW Command

    N
    NOTE Command

    O
    ON BAR Command

    ON ERROR Command

    ON ESCAPE Command

    ON EXIT BAR Command

    ON EXIT MENU Command

    ON EXIT PAD Command

    ON EXIT POPUP Command

    ON KEY = Command

    ON KEY Command

    ON KEY LABEL Command

    ON PAD Command

    ON PAGE Command

    ON READERROR Command

    ON SELECTION BAR Command

    ON SELECTION MENU Command

    ON SELECTION PAD Command

    ON SELECTION POPUP Command

    ON SHUTDOWN Command

    OPEN DATABASE Command

    P
    PACK Command

    PACK DATABASE Command

    PARAMETERS Command

    PLAY MACRO Command

    POP KEY Command

    POP MENU Command

    POP POPUP Command

    PRINTJOB … ENDPRINTJOB Command

    PRIVATE Command

    PROCEDURE Command

    PUBLIC Command

    PUSH KEY Command

    PUSH MENU Command

    PUSH POPUP Command

    Q
    QUIT Command

    R
    RD | RMDIR Command

    READ Command

    READ EVENTS Command

    READ MENU Command

    RECALL Command

    REGIONAL Command

    REINDEX Command

    RELEASE BAR Command

    RELEASE CLASSLIB Command

    RELEASE Command

    RELEASE LIBRARY Command

    RELEASE MENUS Command

    RELEASE PAD Command

    RELEASE POPUPS Command

    RELEASE PROCEDURE Command

    RELEASE WINDOWS Command

    REMOVE CLASS Command

    REMOVE TABLE Command

    RENAME CLASS Command

    RENAME Command

    RENAME CONNECTION Command

    RENAME TABLE Command

    RENAME VIEW Command

    REPLACE Command

    REPLACE FROM ARRAY Command

    REPORT Command

    RESTORE FROM Command

    RESTORE MACROS Command

    RESTORE SCREEN Command

    RESTORE WINDOW Command

    RESUME Command

    RETRY Command

    RETURN Command

    ROLLBACK Command

    RUN | Command

    S
    SAVE MACROS Command

    SAVE SCREEN Command

    SAVE TO Command

    SAVE WINDOWS Command

    SCAN … ENDSCAN Command

    SCATTER Command

    SCROLL Command

    SEEK Command

    SELECT – SQL Command

    SELECT Command

    SET ALTERNATE Command

    SET ANSI Command

    SET ASSERTS Command

    SET AUTOSAVE Command

    SET BELL Command

    SET BLOCKSIZE Command

    SET BORDER Command

    SET BROWSEIME Command

    SET BRSTATUS Command

    SET CARRY Command

    SET CENTURY Command

    SET CLASSLIB Command

    SET CLEAR Command

    SET CLOCK Command

    SET COLLATE Command

    SET COLOR OF Command

    SET COLOR OF SCHEME Command

    SET COLOR SET Command

    SET COLOR TO Command

    SET Command

    SET COMPATIBLE Command

    SET CONFIRM Command

    SET CONSOLE Command

    SET COVERAGE Command

    SET CPCOMPILE Command

    SET CPDIALOG Command

    SET CURRENCY Command

    SET CURSOR Command

    SET DATABASE Command

    SET DATASESSION Command

    SET DATE Command

    SET DEBUG Command

    SET DEBUGOUT Command

    SET DECIMALS Command

    SET DEFAULT Command

    SET DELETED Command

    SET DELIMITERS Command

    SET DEVELOPMENT Command

    SET DEVICE Command

    SET DISPLAY Command

    SET DOHISTORY Command

    SET ECHO Command

    SET ESCAPE Command

    SET EVENTLIST Command

    SET EVENTTRACKING Command

    SET EXACT Command

    SET EXCLUSIVE Command

    SET FDOW Command

    SET FIELDS Command

    SET FILTER Command

    SET FIXED Command

    SET FORMAT Command

    SET FULLPATH Command

    SET FUNCTION Command

    SET FWEEK Command

    SET HEADINGS Command

    SET HELP Command

    SET HELPFILTER Command

    SET HOURS Command

    SET INDEX Command

    SET INTENSITY Command

    SET KEY Command

    SET KEYCOMP Command

    SET LIBRARY Command

    SET LOCK Command

    SET LOGERRORS Command

    SET MACKEY Command

    SET MARGIN Command

    SET MARK OF Command

    SET MARK TO Command

    SET MEMOWIDTH Command

    SET MESSAGE Command

    SET MULTILOCKS Command

    SET NEAR Command

    SET NOCPTRANS Command

    SET NOTIFY Command

    SET NULL Command

    SET NULLDISPLAY Command

    SET ODOMETER Command

    SET OLEOBJECT Command

    SET OPTIMIZE Command

    SET ORDER Command

    SET PALETTE Command

    SET PATH Command

    SET PDSETUP Command

    SET POINT Command

    SET PRINTER Command

    SET PROCEDURE Command

    SET READBORDER Command

    SET REFRESH Command

    SET RELATION Command

    SET RELATION OFF Command

    SET REPROCESS Command

    SET RESOURCE Command

    SET SAFETY Command

    SET SECONDS Command

    SET SEPARATOR Command

    SET SKIP Command

    SET SKIP OF Command

    SET SPACE Command

    SET STATUS BAR Command

    SET STATUS Command

    SET STEP Command

    SET STRICTDATE Command

    SET SYSFORMATS Command

    SET SYSMENU Command

    SET TALK Command

    SET TEXTMERGE Command

    SET TEXTMERGE DELIMITERS Command

    SET TOPIC Command

    SET TOPIC ID Command

    SET TRBETWEEN Command

    SET TYPEAHEAD Command

    SET UDFPARMS Command

    SET UNIQUE Command

    SET VIEW Command

    SET VOLUME Command

    SET WINDOW OF MEMO Command

    SHOW GET Command

    SHOW GETS Command

    SHOW MENU Command

    SHOW OBJECT Command

    SHOW POPUP Command

    SHOW WINDOW Command

    SIZE POPUP Command

    SIZE WINDOW Command

    SKIP Command

    SORT Command

    STORE Command

    SUM Command

    SUSPEND Command

    SYS(2001) – SET … Command Status

    T
    TEXT … ENDTEXT Command

    TOTAL Command

    TYPE Command

    U
    UNLOCK Command

    UPDATE – SQL Command

    UPDATE Command

    USE Command

    V
    VALIDATE DATABASE Command

    W
    WAIT Command

    WITH … ENDWITH Command

    X
    Y
    Z
    ZAP Command

    ZOOM WINDOW Command

    Visual FoxPro IDE Enhancements

    This version of Visual FoxPro includes many enhancements to the Interactive Development Environment (IDE), including changes to the window behavior, Options dialog box, and system menus.

    Docking Windows
    Visual FoxPro provides docking functionality to its core IDE windows. When you drag a dockable window to a Visual FoxPro window boundary, it reconfigures against the boundary you choose. There are three types of docking behavior supported. For details, see Docking Windows.

    Normal Docking Windows dock to a boundary of the main Visual FoxPro window.
    Linked Docking Windows dock to each other and simultaneously share a dockable window container.
    Tabbed Docking Windows dock to each other and share the full window through tabs.
    The following windows are dockable:

    Command window
    Document View window
    Data Session window
    Properties window
    Debugger window
    Watch window
    Trace window
    Output window
    Locals window
    Call Stack window
    Document View Window
    The new Document View window makes it possible for you to view and navigate to any procedure, function, #DEFINE definition or preprocessor directive in your program or class. The Document View window is a more flexible modeless window that replaces the Procedures/Functions model dialog box that was available in previous versions of Visual FoxPro. For details, see Document View Window.

    Properties Windows
    The following enhancements have been added to the Properties window. For details, see Properties Window.

    The Properties window can now be activated without the form/class designer opened. Properties of the desktop (_SCREEN) are displayed even if the form/class designer is not active.
    ActiveX control properties, events, and methods are now displayed in a different color to distinguish them from native ones.
    Methods and events that have code in parent classes now show inherited class information.
    The Properties window is now available from the Windows menu and the Standard toolbar.
    Command Window
    The contents of the Command window are saved to a file, _command.prg. You can clear the window by selecting Clear from the Shortcut menu. The file is read only at startup, so it can be used by multiple instances of Visual FoxPro. For details, see Command Window.

    Standard Toolbar
    The Standard toolbar now provides easy access to common Visual FoxPro tools, including the Properties window, Document View window, Class Browser, and Object Browser.

    Report Designer
    The Report Designer is much more accessible through the keyboard and now includes the following functionality. For details, see Report Designer.

    Bands menu option
    Makes it possible for you to access the dialog boxes for the properties of individual bands.
    Insert Control menu option
    Makes it possible for you to select controls to place in your report.
    Control of Foreground and Background Colors
    Makes it possible for you to manage foreground and background colors through additions to the Format menu, when the Report Designer is open.
    The Report Designer also includes new keyboard navigation:

    CTRL+TAB to toggle in and out of Tab mode.
    TAB and SHIFT+TAB to move between report objects.
    CTRL+E to toggle into and ESC to toggle out of Label editing.
    Options Dialog Box
    A number of enhancements have been added to the Options dialog box to offer improved use of the IDE. For details, see Options Dialog Box.

    View Tab
    You can now control the number of Most Recently Used files that are displayed in the File menu and in IntelliSense.
    File Locations Tab
    The File Locations tab makes it possible for you to specify the paths of the following additional Visual FoxPro items: File Type Description
    FoxCode Table Specifies the location of FoxCode.dbf for IntelliSense records. For more information, see _FoxCode System Variable and Customizing IntelliSense Using FoxCode.
    FoxTask Table Specifies the location of the table that retains current TaskList shortcut records. For details, see _FoxTask System Variable.
    IntelliSense Manager Specifies the location of the program assigned to the _Codesense System Variable.
    Task List Specifies the location of the program assigned to the _TASKLIST System Variable.
    Object Browser Specifies the location of the program assigned to the _ObjectBrowser System Variable.

    IDE Tab
    The IDE tab makes it possible for you to specify formatting, Save, appearance, and behavior settings for Visual FoxPro file types. The Edit, Format, Tools, and Window menus incorporate changes to add editor and other functionality to the IDE.
    Miscellaneous IDE Enhancements
    In addition to the above-mentioned improvements, Visual FoxPro now contains a wealth of other improvements, including:

    Your Foxuser.dbf resource file is now opened as shared so that you can run multiple instances or allow multiple users to access it simultaneously.
    The Windows menu has a new Cascading menu item to allow you to have your windows reorganized on your desktop in a cascading fashion.
    The form/class designer allows you to more easily drilldown into container classes such as pageframes. By holding down the CTRL or CTRL+SHIFT keys, you can now click directly on a control within a container to select it.
    In the form/class designer, you can hold down the CTRL or CTRL+SHIFT keys, and use the arrow keys to move or resize a control by the amount of your grid scale associated with the Snap-to-Grid setting.
    Additional information and value tips have been added to a number of design surfaces. Extended information is displayed for fields in the database and view designers and the data environment. The Method editor displays value tips for events and methods in the procedures drop-down list. The Watch and Locals debug windows have value tips for expressions that exceed the width of the column displaying the value.
    WAIT WINDOW and Visual FoxPro System dialog boxes (for example, error) now inherit their font settings from your Windows Appearance setting in the Display control panel. Additionally, you can use CTRL+C to copy the contents of a System dialog box to the Clipboard.
    See Also
    Visual FoxPro Interactive Development Environment | Task List Manager | IntelliSense Overview | Visual FoxPro Editor Enhancements | Editor Tab, Tools Options Dialog Box | File Locations Tab, Tools Options Dialog Box | IDE Tab, Tools Options Dialog Box | Report Designer | Docking Windows | Command Window | Document View Window | Data Session Window | Properties Window | Debugger Window | Watch Window | Trace Window | Debug Output Window | Locals Window | Call Stack Window

    Visual FoxPro Editor Enhancements

    The Visual FoxPro Editor includes many enhancements to make it easier to edit and examine code including additional functionality and redistribution of settings and controls.

    The following IDE changes support some of the added functionality of the Visual FoxPro Editor.

    Bookmarks and Shortcuts

    The Visual FoxPro Editor now displays a selection margin on the left side of the window so you can highlight a line and specify breakpoints, bookmarks or shortcuts. For details, see Creating Bookmarks and Shortcuts.

    Bookmarks reference a specific line of source code you might want to return to for further editing or viewing. You can add a shortcut from the selection margin or Editor menu. Bookmarks are not saved between sessions of Visual FoxPro.

    Similar to Bookmarks, Shortcuts are persistent code markers that are stored in the Foxtask table and can be accessed using the Task List Manager.

    Find Dialog Box

    The Find dialog box has new support for performing searches using wildcard pattern matching. Additionally, you can perform searches using several new keyboard shortcuts without opening up the Find dialog box.

    Embedded Hyperlinks

    Visual FoxPro Editor supports embedding and enabling hyperlinks. When you include any valid hyperlink protocol trigger, Visual FoxPro applies hyperlink attributes to the entered text. For details, see “Embedding Hyperlinks” in Dynamic Information Sharing.

    Options Dialog Box

    The Options dialog box has the following enhancements: Editor Tab The Editor tab of the Options dialog box provides the functionality of the Syntax coloring tab, as well as additional options related to the Visual FoxPro Editor. IDE Tab The IDE tab makes it possible for you to specify settings for various types of editors used by Visual FoxPro, including programs, methods, stored procedures, text files and memos. These settings are the defaults used for a new document of a specific type. Once a document is created, its specific settings are stored in the Foxuser resource file, so they can be restored when that document is opened again.

    An Override check box provides the ability to globally override a specific document’s settings stored in the Foxuser resource file.

    If you use Visual FoxPro editors in your distributed applications, you can control their functionality by limiting access to the IntelliSense functions and disabling the availability of hyperlinks.

    Miscellaneous Editor Enhancements

    In addition to the previously mentioned improvements, this version of Visual FoxPro contains a wealth of other improvements including:

    • In this version of Visual FoxPro, the editor opens files as DENY WRITE rather than DENY READ, so a file can be read or copied even while it is open in the Visual FoxPro Editor.
    • You can swiftly selections of text to upper or lower case from the Format or Editor shortcut menus.
    • The Format menu has new options to toggle word wrap and viewing white space (tab, space and paragraph marks).
    • A dirty file indicator mark (asterisk) is now displayed next to the file name in the title bar of the editor window if the document contains any unsaved changes.
    • You can now control whether indentation inserts tabs or spaces. The amount of the indentation can also be set.
    • The characters used for comment strings when you select Comment from the Format or Editor shortcut menus can be set in the Options dialog box.
    • While editing source code, the editor now highlights the parameter contents of a function, when you type a close parenthesis character. The duration of the highlighting can be set in the Options dialog box.
    • A number of new keyboard shortcuts have been added to the editor to improve developer productivity. See Keyboard Shortcuts topic for more details.

    IntelliSense in Visual FoxPro

    IntelliSense displays information in windows and lists that assists you with statement and function completion syntax and displays the available variables, objects, properties, methods, and events of objects.

    In Visual FoxPro, although IntelliSense always is available for native commands and functions, strong typing allows full IntelliSense support in editor windows for all user-defined code elements.

    The following are some features of IntelliSense in Visual FoxPro:

    • When you type a table-related keyword in the Visual FoxPro Editor or in the Command window, the Auto Table/Field drop-down list displays the valid members in the current context.
    • When you type one of the listed commands in the Command window, Visual FoxPro displays a list of the appropriate MRU files.
    • You can access IntelliSense functionality automatically for containers and controls in Visual FoxPro visual designers, such as the Form designer.
    • IntelliSense functionality is activated as you type in the Visual FoxPro Editor, in code editor windows for methods, and in the Command window.
    • Use the Visual FoxPro IntelliSense Manager Window to modify the appearance and behavior of the IntelliSense functionality.
    • You can modify or add to the functionality of IntelliSense in Visual FoxPro by modifying the contents of FoxCode.dbf.
    • You can enable or disable IntelliSense functionality programmatically by setting the _VFP.EditorOptions property value. For more information, see EditorOptions Property.

    Task List Manager

    The Visual FoxPro Task List Manager makes it possible for you to keep track of Shortcuts to locations in your code that you might want to return to for further editing. Additionally, the Task List Manager makes it possible for you organize your personal and business to-do lists in a manner similar to Microsoft Outlook.

    By Amjad Izhar
    Contact: amjad.izhar@gmail.com
    https://amjadizhar.blog

  • Visual FoxPro 6 Help Documentation – Study Notes

    Visual FoxPro 6 Help Documentation – Study Notes

    Welcome to Microsoft Visual FoxPro. Visual FoxPro is the object-oriented relational database management system that makes it possible for you to create database solutions for the desktop to the Web. Visual FoxPro provides powerful data handling capabilities, rapid application development tools for maximum productivity, and the flexibility needed to build all types of database solutions.

    Visual FoxPro 6 Language Reference Book on Archive.Org

    Guidelines for Using Visual FoxPro Foundation Classes

    The Visual FoxPro .vcx visual class libraries located in the \Ffc\ folder contain a variety of foundation classes for enhancing your Visual FoxPro applications with little or no programming. You can freely distribute the foundation classes with your applications. These foundation classes are contained in the Component Gallery. The Component Gallery provides a quick and easy way to learn more about the properties, events, and methods of each of the foundation classes.

    You can also open up a foundation class in the Class Designer or Class Browser to see its structure and code. This is a great way to learn how the foundation class works as well as offering excellent insights into programming with Visual FoxPro.

    The following guidelines provide information about how you can add the Visual FoxPro foundation classes to your applications.

    Class Types

    You need to know the Visual FoxPro base class of a foundation class before you can add the foundation class to your application. Certain foundation classes can be used only as visual objects on forms, while others are non-visual and can be run programmatically without being placed on a form. The Visual FoxPro Foundation Classes documentation indicates the base class of each foundation class so you can determine if the foundation class can be added to a form, or run programmatically in your application. Note that in the Component Gallery you can right-click a foundation class to display a shortcut menu. Choose Properties from the shortcut menu, and then choose the Class tab to display the base class.

    The following table lists the Visual FoxPro base classes and how they can be added to your applications.

    Category A – base classes that can be dropped onto a formCategory B – base classes that can be dropped onto a form or run programmatically in your applicationCategory C – base classes that can only be run programmatically in your application
    CheckboxCustomForm
    ComboboxContainerFormset
    CommandbuttonTimerToolbar
    CommandgroupProjectHook 
    EditboxActiveDoc 
    Grid  
    Hyperlink  
    Image  
    Label  
    Line  
    Listbox  
    OLE Control  
    Optionbutton  
    Optiongroup  
    Shape  
    Spinner  
    Textbox  

    Adding Foundation Classes to Forms

    You will most often add foundation classes to forms. You can drag and drop foundation classes from the Component Gallery, Class Browser, Project Manager, and the Forms Control toolbar onto forms.

    Note   You can select a foundation class you’ve added to a form and then choose Class Browser from the Tools menu to display more information about the foundation class.

    Component Gallery   The Component Gallery provides the easiest way to add foundation classes to a form. For foundation classes with Category A and B base classes, you can drag the foundation class from the Component Gallery and then drop it on a form. You can also right-click a foundation class in the Component Gallery to display a shortcut menu, and then choose Add to Form to add the foundation class to the form.

    Some of the foundation classes have associated builders that are automatically launched to prompt you for more information needed by the foundation class.

    Class Browser   You can drag foundation classes with Category A and B base classes directly from the Class Browser to a form by using the drag icon in the upper left corner of the Class Browser. Select the foundation class in the Class Browser, click the icon for the foundation class in the upper left corner of the Class Browser, and then drag the icon over the form. Release the mouse button over the form where you’d like the foundation class to appear on the form.

    Foundation classes dragged from the Class Browser to a form do not launch the associated builder. However, you can launch the builder by after the foundation class has been dropped on the form. Select the foundation class on the form, and then right-click to display the shortcut menu. Choose Builder from the shortcut menu to launch the builder.

    Project Manager   Foundation classes with Category A and B base classes can be dragged from the Project Manager and dropped on a form.

    Foundation classes dragged from the Project Manager to a form do not launch the associated builder. However, you can launch the builder by after the foundation class has been dropped on the form. Select the foundation class, and then right-click to display the shortcut menu. Choose Builder from the shortcut menu to launch the builder.

    Form Controls toolbar   Foundation classes with Category A and B base classes added to the Form Controls toolbar can be added to a form.

    If the Builder Lock isn’t on, foundation classes dropped from the Form Controls toolbar may launch an associated builder. If the Build Lock is on, you can launch the builder by after the foundation class has been added to the form. Select the foundation class on the form, and then right-click to display the shortcut menu. Choose Builder from the shortcut menu to launch the builder.

    Adding Foundation Classes to Projects

    When a form containing foundation classes is added to a Visual FoxPro project, the Project Manager automatically adds the visual class libraries containing the foundation classes to the project. However, there are other cases where you may need to add foundation classes to a project. For example, your application may run a Category C foundation class, so the foundation class must be added to the application’s project.

    Foundation classes can be added to a project from the Component Gallery, by dragging the .vcx visual class library containing the foundation classes from the Windows Explorer, or by choosing the Add button in the Project Manager.

    Adding Foundation Classes from the Component Gallery

    You can drag a foundation class from the Component Gallery to a project, or you can right-click the foundation class in the Component Gallery to display a shortcut menu, and then choose Add to Project to add the foundation class to the project. When you add a foundation class to a project, the Add Class to Project dialog box is displayed, prompting you with the following options: Add class to project Choose this option to add the foundation class and its .vcx class library to the project. Again, this is done automatically for classes dropped onto a form (Categories A and B). For certain Category B and C classes where you plan to call them programmatically from within your application, you will want to choose this option. Create a new class from selected class Choose this option to create a new subclass from the foundation class you want to add to the project. This option makes it possible for you to enhance the functionality of the original foundation class, usually by adding additional program code. Create a new form from selected class Choose this option for foundation classes with a Form base class (for example, the foundation classes in _dialogs.vcx). This option makes it possible for you to create a new form from the foundation class and enhance the functionality of the original foundation class.

    Adding Foundation Classes from the Windows Explorer

    A foundation class can be added to a project by dragging the .vcx visual class library containing the foundation class from the Window Explorer to the Project Manager. The visual class library is added to the Class Libraries item in the Project Manager.

    Adding Foundation Classes from within the Project Manager

    A foundation class can be added to a project by selecting the Classes tab and then choosing the Add button. Select the class library from the \Ffc\ folder that contains the foundation class to add to the project.

    Incorporating Classes into your Application

    In many situations, most foundation classes don’t require additional programming to work with your application. However, you may need to provide additional program code for certain foundation classes (those of Category B and Category C non-visual base classes).

    Non-Visual Foundation Classes

    For example, foundation classes are often based on the Category B Custom class, and these require additional programming. These non-visual classes often perform common tasks such as checking information in the Windows registry, handling environment settings, managing application errors, and utilizing Automation with other applications, such as performing mail merge with Microsoft Word.

    You can drop these non-visual classes onto a form, but you will need to do some additional work in order for them to work with your application. In some cases, a builder is launched when you drag a foundation class onto a form.

    The following example demonstrates some of the program code typically necessary to use a non-visual foundation class in your application:

    1. Drag the File Version foundation class from the Component Gallery (Foundation Classes\Utilities folder) onto a form.
    2. Add a command button to the form and add the following code to its Click event: WITH THISFORM._FILEVERSION1 .cFileName = HOME( )+ ‘VFP7.EXE’ .GetVersion( ) .DisplayVersion( ) ENDWITH
    3. Run the form and click the command button.

    You can incorporate a non-visual class in your application without dropping it on a form, as long as you include it in the project used to create the application. The following code illustrates how to executed this same File Version foundation class if the class is not dropped onto a form.

    LOCAL oFileVersion
    oFileVersion = NewObject('_fileversion', '_utilities.vcx')
    WITH oFileVersion
       .cFileName = HOME( )+ 'VFP7.EXE'
       .GetVersion( )
       .DisplayVersion( )
    ENDWITH

    Note   This example assumes that the code can locate the _utilities.vcx class library or from an .app file that is built containing _utilities.vcx.

    When you use a non-visual foundation class, you need to know how and when the class is used within your application so it can be scoped correctly. If only a single form uses the class, you can just drag the class onto the form. However, if the class is used by many forms or is used globally by the application, the foundation class should have a global scope in the application so it remains accessible throughout the application. A global scope may also improve performance.

    Visual Foundation Classes

    You can also programmatically add visual foundation classes, such as those with form base classes, to your application. The following example shows how you can add code to your application to display an About dialog box box.

    LOCAL oAbout
    oAbout = NewObject('_aboutbox','_dialogs.vcx')
    oAbout.Show( )

    You can create a subclass of the dialog box foundation class for each of your applications so that you can customize the contents of the dialog box for each application. The following example demonstrates how you can subclass the Aboutbox foundation class:

    1. Drag and drop the Aboutbox class from the Component Gallery (Foundation Classes\Dialogs folder) to the project for your application.
    2. Select Create new form from selected class in the Add Class to Project dialog box that is displayed, and enter name for the form.
    3. Change the Caption property for the new form for your application. Save and close the form.
    4. Add program code (DO FORM FormName) to the procedure that runs the form, such as an About menu item procedure. -or-

    Drag the Run Form button class from the Component Gallery (Foundation Classes\Buttons folder) onto the form. A builder is launched, and you can specify the name of the form to execute.

    If you use the Visual FoxPro 7.0 Application Framework, the Application Builder automatically handles adding forms (both .scx and .vcx form classes). The new Application Wizard or the Component Gallery New Application item installs this framework in the projects they create. The Application Builder interacts directly with the framework and enables you to specify how and where the form is launched.

    By using a framework built with the Application Wizard, the Application Builder, and Component Gallery, you have a rich set of tools for creating entire applications with minimal manual coding.

    Class Naming Conventions

    The Visual FoxPro Foundation classes and their properties and methods use the following naming conventions.

    Classes and Class Libraries

    Most foundation classes are subclassed from classes in the _base.vcx visual class library, which you can also find in the \Ffc\ folder. The naming conventions for these classes reflect the base class used. For example, a subclass of the Custom class is called _Customin _base.vcx. All classes use an underscore ( _ ) to preface the name of a class in _base.vcx.

    A few class libraries do not contain classes that are subclassed from _base.vcx because these classes are shared with other Visual FoxPro components such as wizards and builders. These classes are contained in class libraries without a preceding underscore, such as Registry.vcx.

    Methods and Properties

    Methods are often based on an action name such as RunForm. If the name contains several words, for example, RunForm, then capitalization reflects this. Properties are usually prefaced with a single letter characterizing the data type of that particular property. For example, cFileName indicates that the property is of character type. In addition, default values for properties are also set to the appropriate data type. For example, a logical property can be initialized to false (.F.), while a numeric property can be initialized to 0.

    Properties of classes that shipped in earlier versions of Visual FoxPro do not strictly adhere to these property-naming conventions, and retain their earlier names to avoid compatibility conflicts with user code referencing these properties.

    Enhancing or Modifying FoxPro Foundation Classes

    You can enhance or modify the Visual FoxPro foundation classes to meet the needs of your application. However, we recommend that you do not modify the foundation classes themselves. The foundation classes may be periodically updated with new functionality.

    Subclassing the Foundation Class

    The source code is provided for the foundation classes, so you can subclass any foundation class to override or enhance properties and methods. This choice is common when the behavior of a particular foundation class varies between different applications. One application might use a foundation class directly, while another application uses a subclass of the foundation class.

    Updating _base.vcx

    If you want to add global changes to the Visual FoxPro foundation classes, you can modify _base.vcx. Since foundation classes are subclassed from _base.vcx, changes to this class library are automatically propagated to the foundation classes. A common set of methods and properties are provided for all the classes in _base.vcx. However, you can alter the classes in _base.vcx if they add desired behavior to your applications (unlike the foundation classes that we recommend that you do not change).

    Instead of changing _base.vcx, however, you should redefine the classes in _base.vcx to inherit their behavior from your own custom base classes (rather than from the Visual FoxPro base classes currently used). If you already have a custom class library which subclasses the Visual FoxPro base classes, you can redefine the classes in _base.vcx to inherit from your custom classes so that when components use the _base classes they will inherit from your custom classes too. You can use the Class Browser to redefine the parent class for a particular class.

    Note   If you redefine the classes to inherit from your own custom base classes, you should add DODEFAULT( ) calls at appropriate locations if you desire that parent class method code be executed.

    If you replace the entire _base.vcx class with your own, make sure that you have the same set of named classes; otherwise the foundation classes will have missing links.

    See Also

    Visual FoxPro Foundation Classes A-Z | Visual FoxPro Foundation Classes | File Version | About Dialog Box Foundation Class | Run Form button

    Visual FoxPro 6 Commands

    & Command

    Performs macro substitution.

    & VarName[.cExpression]

    Parameters

    & VarName

    Specifies the name of the variable or array element to reference in the macro substitution. Do not include the M. prefix that distinguishes variables from fields. Such inclusion causes a syntax error. The macro should not exceed the maximum statement length permitted in Visual FoxPro.

    A variable cannot reference itself recursively in macro substitution. For example, the following generates an error message:

     Copy Code
    STORE ‘&gcX’ TO gcX ? &gcX

    Macro substitution statements that appear in DO WHILE, FOR, and SCAN are evaluated only at the start of the loop and are not reevaluated on subsequent iterations. Any changes to the variable or array element that occur within the loop are not recognized. [. cExpression]

    The optional period (.) delimiter and .cExpression are used to append additional characters to a macro. cExpression appended to the macro with .cExpression can also be a macro. If cExpression is a property name, include an extra period (cExpression..PropertyName).

    Remarks

    Macro substitution treats the contents of a variable or array element as a character string literal. When an ampersand (&) precedes a character-type variable or array element, the contents of the variable or element replace the macro reference. You can use macro substitution in any command or function that accepts a character string literal.

    Tip:
    Whenever possible, use a name expression instead of macro substitution. A name expression operates like macro substitution. However, a name expression is limited to passing character strings as names. Use a name expression for significantly faster processing if a command or function accepts a name (a file name, window name, menu name, and so on). For additional information on name expressions, see Name Expression Creation.

    While the following commands are acceptable:

     Copy Code
    STORE ‘customer’ TO gcTableName STORE ‘company’ TO gcTagName USE &gcTableName ORDER &gcTagName

    use a name expression instead:

     Copy Code
    USE (gcTableName) ORDER (gcTagName)

    Macro substitution is useful for substituting a keyword in a command. In the following example, the TALK setting is saved to a variable so the setting can be restored later in the program. The original TALK setting is restored with macro substitution.

    Note:
    Performing concatenation with a single ampersand (&) when attempting to include double ampersands (&&) in a string literal might produce undesirable results. For example, suppose you assign the string “YYY” to a variable, BBB. Performing concatenation using “AAA&” and “&BBB” replaces “BBB” with “YYY” so instead of getting the result “AAA&&BBB”, the result is “AAA&YYY”. For more information, see && Command.

    Example

     Copy Code
    STORE SET(‘TALK’) TO gcSaveTalk
    SET TALK OFF
    * * Additional program code *
    SET TALK &gcSaveTalk && Restore original TALK setting

    See Also

    Concepts

    Commands (Visual FoxPro)

    STORE Command

    && Command

    Macro Substitution (Visual FoxPro)

    && Command

    Indicates the beginning of a nonexecuting inline comment in a program file.

    && [Comments]

    Parameters

    && [ Comments]

    Specifies inline comments that follow.

    Remarks

    Inserting inline comments to denote the end of the IF … ENDIF, DO, and FOR … ENDFOR structured programming commands greatly improves the readability of programs when including many such structures.

    Caution:
    Including double ampersands (&&) in a string literal, for example, “AAA&&BBB”, generates an error. Instead, to include double ampersands, use concatenation as shown: “AAA&” + “&” + “BBB”.
    Note:
    When using concatenation, use caution with placement of a single ampersand (&), which is used to perform macro substitution and thus might produce undesirable results. For example, suppose you assign the string “YYY” to a variable, BBB. Performing concatenation using “AAA&” and “&BBB” replaces “BBB” with “YYY”, so instead of getting the result “AAA&&BBB”, the result is “AAA&YYY”. For more information, see & Command.

    To continue a comment on the following line, place a semicolon (;) at the end of the comment line to be continued.

    Note:
    In earlier versions of Visual FoxPro, you cannot place && and a comment after the semicolon that is used to continue a command line to an additional line.

    Example

    The following example includes the inline comments “20 years of monthly payments” indicated by the && command:

     Copy Code
    STORE (20*12) TO gnPayments && 20 years of monthly payments NOTE Initialize the page number; variable. STORE 1 to gnPageNum * Set up the loop DO WHILE gnPageNum <= 25 && loop 25 times gnPageNum = gnPageNum + 1 ENDDO && DO WHILE gnPageNum <= 25

    See Also

    Concepts

    * Command

    MODIFY COMMAND Command

    MODIFY FILE Command

    NOTE Command

    & Command

    Commands (Visual FoxPro)

    * Command

    Indicates the beginning of a nonexecuting comment line in a program file.

    * [Comments]

    Parameters

    Comments

    Specifies the comment in the comment line. For example:

     Copy Code
    * This is a comment

    Remarks

    Place a semicolon (;) at the end of each comment line that continues to a following line.

    Any text added to a method or event in a Visual Class Library (VCX) or form (SCX) Code window will cause that class to have Override behavior for the method or event. Therefore, code for the method or event in a parent class will not be executed by default (it must be explicitly executed). This includes non-executable comment lines that begin with “*”.

    Example

     Copy Code
    * Initialize the page number; variable. STORE 1 to gnPageNum * Set up the loop DO WHILE gnPageNum <= 25 && loop 25 times gnPageNum = gnPageNum + 1 ENDDO && DO WHILE gnPageNum <= 25

    See Also

    Concepts

    && Command

    MODIFY COMMAND Command

    MODIFY FILE Command

    NOTE Command

    Commands (Visual FoxPro)

    Overriding and Calling Parent Class Code

    ? | ?? Command

    Evaluates expressions and sends the results to the main Visual FoxPro window, an active user-defined window, or the printer.

    ? | ?? Expression1 [PICTURE cFormatCodes] | [FUNCTION cFormatCodes] | [VnWidth] [AT nColumn] [FONT cFontName [, nFontSize [, nFontCharSet]] [STYLE cFontStyle | Expression2]] [, Expression3] …

    Parameters

    ? Expression1

    Evaluates the expression specified by Expression1 and sends a carriage return and line feed preceding the expression results.

    The results display on the next line of the main Visual FoxPro window or the active user-defined window and are printed at the left margin of a page unless a function code specified by cFormatCodes or the _ALIGNMENT system variable specifies otherwise.

    If you omit the expressions, a blank line is displayed or printed. A space is placed between expression results when multiple expressions are included.

    If Expression1 is an object, the ? command returns the character string, “(Object)”. ?? Expression1

    Evaluates the expression specified by Expression1 and displays the expression results on the current line at the current position of the main Visual FoxPro window, an active user-defined window, or the printer. A carriage return and line feed are not sent before the results. PICTURE cFormatCodes

    Specifies a picture format in which the result of Expression1 is displayed. cFormatCodes can consist of function codes, picture codes, or a combination of both. You can use the same codes available in the Format Property and InputMask Property.

    Function codes affect the overall format of the result; picture codes act on individual characters in the result. If function codes are used in cFormatCodes, they must appear before the picture codes and they must be preceded by an at (@) sign. Multiple function codes with no embedded spaces can immediately follow the @ sign. The last function code must be followed by one or more spaces. The space or spaces signal the end of the function codes and the start of the picture codes. FUNCTION cFormatCodes

    Specifies a function code to include in the output from the ? and ?? commands. If the function clause is included, do not precede the function codes with an @ sign. Function codes must be preceded by a @ sign when included in PICTURE. V nWidth

    Specifies a special function code that enables the results of a character expression to stretch vertically within a limited number of columns. nWidth specifies the number of columns in the output.

     Copy Code
    ? ‘This is an example of how the V function code works.’ ; FUNCTION ‘V10’

    AT nColumn

    Specifies the column number where the output is displayed. This option makes it possible for you to align output in columns to create a table. The numeric expression nColumn can be a user-defined function that returns a numeric value. FONT cFontName[, nFontSize [, nFontCharSet]]

    Specifies a font for output by the ? or ?? command. cFontName specifies the name of the font, and nFontSize specifies the point size. You can specify a language script with nFontCharSet. See the GETFONT( ) Function for a list of available language script values.

    For example, the following command displays the system date in 16-point Courier font:

     Copy Code
    ? DATE( ) FONT ‘Courier’,16

    If you include the FONT clause but omit the point size nFontSize, a 10-point font is used.

    If you omit the FONT clause, and output for the ? or ?? command is placed in the main Visual FoxPro window, the main Visual FoxPro window font is used for the output. If you omit the FONT clause, and output for the ? or ?? command is placed in a user-defined window, the user-defined window font is used for the output.

    Note:
    If the font you specify is not available, a font with similar font characteristics is substituted.

    STYLE cFontStyle

    Specifies a font style for output by the ? or ?? commands. If you omit the STYLE clause, the Normal font style is used. If the font style you specify is not available, a font style with similar characteristics is substituted.

    Note:
    You must include the FONT clause when you specify a font style with the STYLE clause.

    The following table lists font styles that you can specify for cFontStyle.

    cFontStyleFont style
    BBold
    IItalic
    NNormal
    QOpaque
    Strikeout
    TTransparent
    UUnderline

    You can include more than one character to specify a combination of font styles. For example, the following command displays the system date in Courier Bold Italic:

     Copy Code
    ? DATE( ) FONT ‘COURIER’ STYLE ‘BI’

    Remarks

    To send the results to the printer only, use the following commands:

     Copy Code
    SET PRINTER ON SET CONSOLE OFF

    To send the results to the printer and the main Visual FoxPro window or an active user-defined window, use the following command:

     Copy Code
    SET PRINTER ON

    The setting of SET ALTERNATE affects the destination for the ? and ?? commands. For more information, see SET ALTERNATE Command.

    The ? command displays binary data for Varbinary data types in hexadecimal format with no limitation on size. For more information, see Varbinary Data Type.

    Example

    The following example displays evaluates and displays the expressions specified:

     Copy Code
    ? 15 * (10+10) ? ‘Welcome to ‘ PICTURE ‘@!’ ?? ‘Visual FoxPro’

    See Also

    Concepts

    Commands (Visual FoxPro)

    ??? Command

    @ … SAY Command

    _ALIGNMENT System Variable

    SET MEMOWIDTH Command

    SET PRINTER Command

    SET SPACE Command

    Format Property

    InputMask Property

    ??? Command

    Sends output directly to the printer.

    ??? cExpression

    Parameters

    cExpression

    Specifies the characters that are sent to the printer.

    Remarks

    A group of three question marks bypasses the printer driver and sends the contents of cExpression directly to the printer. cExpression must contain valid printer codes.

    Printer control codes make it possible for you to reset the printer, change type styles and sizes, and enable or disable boldface printing. These codes can consist of any combination of printable or nonprintable characters that are specific to the printer you are using. You can direct control codes to the printer in several different ways:

    • Use combinations of CHR( ) and quoted strings concatenated with + to send ASCII characters directly to the printer.
    • Use quotation marks to send a string containing printer codes or ASCII characters.
    • Codes can be sent to the printer before printing begins and after printing ends with the _PSCODE and _PECODE system variables. For more information, see _PSCODE System Variable and _PECODE System Variable.

    Printer control codes vary from printer to printer. The best source for information about printer control codes is the manual that came with your printer.

    See Also

    Concepts

    ? | ?? Command

    @ … SAY Command

    CHR( ) Function

    @ … CLASS Command

    Creates a control or object that can be activated with READ.

    @ nRow, nColumn CLASS ClassName NAME ObjectName

    Parameters

    @ nRow, nColumn

    Specifies the position of the control or object. The height and width of the control or object is determined by the class default height and width values.

    Rows are numbered from top to bottom. The first row is number 0 in the main Visual FoxPro window or in a user-defined window. Row 0 is the row immediately beneath the Visual FoxPro system menu bar.

    Columns are numbered from left to right. The first column is number 0 in the main Microsoft Visual FoxPro window or in a user-defined window. When a control or object is placed in a user-defined window, the row and column coordinates are relative to the user-defined window, not to the main Visual FoxPro window.

    A position in the main Visual FoxPro window or in a user-defined window is determined by the font of the window. Most fonts can be displayed in a wide variety of sizes; some are proportionally spaced. A row corresponds to the height of the current font; a column corresponds to the average width of a letter in the current font.

    You can position the control or object using decimal fractions for row and column coordinates. CLASS ClassName

    Specifies the class of the control or object. ClassName can be a Visual FoxPro base class or a user-defined class. The following table lists the Visual FoxPro base classes you can specify for ClassName. NAME ObjectName

    Specifies the name of the object reference variable to create. The object-oriented properties, events, and methods of the control or object can be manipulated by referencing this variable. For a complete list of the Visual FoxPro base classes, see Base Classes in Visual FoxPro.

    Remarks

    @ … CLASS provides an intermediate step for converting programs and applications created in earlier versions of FoxPro to the preferred object-oriented programming methods of Visual FoxPro. For additional information about backward compatibility with FoxPro 2.x controls, see Controls and Objects Created in Earlier Versions.

    For information about object-oriented programming in Visual FoxPro, see Object-Oriented Programming.

    Example

    The following example demonstrates how @ … CLASS can be used with programming techniques used in earlier FoxPro versions (in this example, use of READ to activate controls). @ … CLASS is used to create a text box whose properties can be changed with the Visual FoxPro object-oriented programming techniques.

    ON KEY LABEL is used to display the Windows Color dialog box when you press CTRL+I. The TextBox is placed on the main Visual FoxPro window using @ … CLASS, and READ activates the text box.

     Copy Code
    CLEAR ON KEY LABEL CTRL+I _SCREEN.PageFrame1.Page1.goFirstName.BackColor; = GETCOLOR( ) @ 2,2 SAY ‘Press Ctrl+I to change the background color’ @ 4,2 CLASS TextBox NAME goFirstName READ CLEAR

    See Also

    Concepts

    Commands (Visual FoxPro)

    CREATEOBJECT( ) Function

    DEFINE CLASS Command

    READ Command

    _SCREEN System Variable

    @ … CLEAR Command

    Clears a portion of the main Visual FoxPro window or a user-defined window.

    @ nRow1, nColumn1 [CLEAR CLEAR TO nRow2, nColumn2]

    Parameters

    @ nRow1, nColumn1 CLEAR

    Clears a rectangular area whose upper-left corner begins at nRow1 and nColumn1 and continues to the lower-right corner of the main Visual FoxPro window or a user-defined window. CLEAR TO nRow2, nColumn2

    Clears a rectangular area whose upper-left corner is at nRow1 and nColumn1 and whose lower-right corner is at nRow2 and nColumn2.

    Remarks

    If you omit CLEAR or CLEAR TO, Visual FoxPro clears nRow1 from nColumn1 to the end of the row.

    Example

    The following example clears the screen, main Visual FoxPro window, or user-defined window from the second row to the bottom of the window.

     Copy Code
    @ 2,0 CLEAR

    The following example clears a rectangular region. The area from row 10 and column 0 to row 20 and column 20 is cleared.

     Copy Code
    @ 10,0 CLEAR TO 20,20

    See Also

    Concepts

    Commands (Visual FoxPro)

    CLEAR Commands

    SET CLEAR Command

    _WClear( ) API Library Routine

    @ … FILL Command

    Changes the colors of existing text within an area of the screen.

    @ nRow1, nColumn1 FILL TO nRow2, nColumn2[COLOR SCHEME nSchemeNumber COLOR ColorPairList]

    Parameters

    @ nRow1, nColumn1

    Specifies the upper-left corner of the area to change. FILL TO nRow2, nColumn2

    Specifies the lower-right corner of the area to change. COLOR SCHEME nSchemeNumber

    Specifies the color of the area. Only the first color pair in the specified color scheme determines the color of the area. COLOR ColorPairList

    Specifies the color of the area. Only the first color pair in the specified color pair list determines the color of the area.

    If you omit the COLOR SCHEME or COLOR clauses, the rectangular portion is cleared. An area can also be cleared with @ … CLEAR.

    Remarks

    This command changes the colors of text within a rectangular area of the main Visual FoxPro window or the active user-defined window. You can set the foreground and background color attributes for existing text only. Any text output to the same area after you issue @ … FILL appears in the default screen or window colors.

    Example

    The following example clears the main Visual FoxPro window and fills an area with color.

     Copy Code
    ACTIVATE SCREEN CLEAR @ 4,1 FILL TO 10, 8 COLOR GR+/B

    See Also

    Concepts

    Commands (Visual FoxPro)

    @ … SAY Command

    ColorScheme Property

    FillColor Property (Visual FoxPro)

    @ … SCROLL Command

    Moves an area of the main Microsoft Visual FoxPro window or a user-defined window up, down, left, or right.

    @ nRow1, nColumn1 TO nRow2, nColumn2 SCROLL [UP | DOWN | LEFT | RIGHT] [BY nMoveAmount]

    Parameters

    @ nRow1, nColumn1 TO nRow2, nColumn2 SCROLL

    Moves a rectangular area whose upper-left corner is at nRow1, nColumn1 and lower-right corner is at nRow2, nColumn2. UP | DOWN | LEFT | RIGHT

    Specifies the direction in which rectangular area is moved. If you omit a direction clause, the area is moved upward. BY nMoveAmount

    Specifies the number of rows or columns the rectangular area is moved. If you omit BY nMoveAmount, the region is moved by one row or column.

    See Also

    Concepts

    Commands (Visual FoxPro)

    SCROLL Command

    Scrolled Event

    \ | \\ Command

    Prints or displays lines of text.

    \TextLine
    \\TextLine

    Parameters

    \ TextLine

    When you use \, the text line is preceded by a carriage return and a line feed. \\ TextLine

    When you use \\, the text line is not preceded by a carriage return and a line feed.

    Any spaces preceding \ and \\ are not included in the output line, but spaces following \ and \\ are included.

    You can embed an expression in the text line. If the expression is enclosed in the text merge delimiters (<< >> by default) and SET TEXTMERGE is ON, the expression is evaluated and its value is output as text.

    Remarks

    The \ and \\ commands facilitate text merge in Visual FoxPro. Text merge makes it possible for you to output text to a file to create form letters or programs.

    Use \ and \\ to output a text line to the current text-merge output file and the screen. SET TEXTMERGE is used to specify the text merge output file. If text merge is not directed to a file, the text line is output only to the main Visual FoxPro window or the active user-defined output window. SET TEXTMERGE NOSHOW suppresses output to the main Visual FoxPro window or the active user-defined window.

    Example

     Copy Code
    CLOSE DATABASES OPEN DATABASE (HOME(2) + ‘Data\testdata’) USE Customer && Open customer table SET TEXTMERGE ON SET TEXTMERGE TO letter.txt \<<CDOW(DATE( ))>>, <<CMONTH(DATE( ))>> \\ <<DAY(DATE( ))>>, <<YEAR(DATE( ))>> \ \ \Dear <<contact>> \Additional text \ \Thank you, \ \XYZ Corporation CLOSE ALL MODIFY FILE letter.txt NOEDIT

    See Also

    Concepts

    _PRETEXT System Variable

    SET TEXTMERGE Command

    SET TEXTMERGE DELIMITERS Command

    _TEXT System Variable

    TEXT … ENDTEXT Command

    Commands (Visual FoxPro)

    = Command

    Evaluates one or more expressions.

    = Expression1 [, Expression2 …]

    Parameters

    Expression1[, Expression2…]

    Specifies the expression or expressions that the = command evaluates.

    Remarks

    The = command evaluates one or more expressions, Expression1, Expression2 …, and discards the return values. This option is particularly useful when a Visual FoxPro function or a user-defined function has a desired effect, but there is no need to assign the function’s return value to a variable, array element, or field.

    For example, to turn insert mode on, you can issue the following command:

     Copy Code
    = INSMODE(.T.)

    INSMODE normally returns a True (.T.) or False (.F.) value. In the example above, the function is executed but the return value is discarded.

    If only one expression (Expression1) is included, the equal sign is optional.

    Note:
    There are two unrelated uses for the equal sign (=). It can be used as an operator in logical expressions to make a comparison, or to assign values to variables and array elements. In these two cases, the equal sign (=) is an operator and not a command. See Relational Operators for more information about using the equal sign (=) as an operator in logical expressions. See STORE Command for more information about using the equal sign (=) to assign values to variables and array elements.

    See Also

    Concepts

    EVALUATE( ) Function

    INSMODE( ) Function

    Relational Operators

    STORE Command

    ON KEY = Command

    Commands (Visual FoxPro)

    ACTIVATE MENU Command

    Displays and activates a menu bar.

    ACTIVATE MENU MenuBarName [NOWAIT] [PAD MenuTitleName]

    Parameters

    MenuBarName

    Specifies the name of the menu bar to activate. NOWAIT

    Specifies that at run time the program should not wait for the user to choose a menu from the active menu bar or to press ESC. Instead, the program continues to execute. A menu activated with the NOWAIT option does not return program execution to the line following the ACTIVATE MENU command when DEACTIVATE MENU is issued. PAD MenuTitleName

    Specifies the menu title name that is automatically selected when the menu bar is activated. If you don’t specify a menu title name, the first menu title name in the activated menu bar is activated by default.

    Remarks

    Displays and activates the menu bar specified with MenuBarName. This command works in conjunction with DEFINE MENU and DEFINE PAD.

    Note:
    When you include the Visual FoxPro system menu bar (_MSYSMENU) in an application, there is no need to activate the menu. Instead, issue SET SYSMENU AUTOMATIC.

    Example

    The following example uses ACTIVATE MENU to display and activate a user-defined menu system. The current system menu bar is first saved to memory with SET SYSMENU SAVE, and then all system menu titles are removed with SET SYSMENU TO.

    Two menu titles are created with DEFINE PAD; DEFINE POPUP is used to create a drop-down menu for each menu title. DEFINE BAR is used to create menu items on each of the menus. When a menu title is chosen, ON PAD uses ACTIVATE POPUP to activate the corresponding menu. ACTIVATE MENU displays and activates the menu bar.

    When a menu item is chosen from a menu, the CHOICE procedure is executed. CHOICE displays the name of the chosen item and the name of the menu containing the item.

     Copy Code
    *** Name this program ACTIMENU.PRG *** CLEAR SET SYSMENU SAVE SET SYSMENU TO ON KEY LABEL ESC KEYBOARD CHR(13) DEFINE MENU example BAR AT LINE 1 DEFINE PAD convpad OF example PROMPT ‘\<Conversions’ COLOR SCHEME 3 ; KEY ALT+C, ” DEFINE PAD cardpad OF example PROMPT ‘Card \<Info’ COLOR SCHEME 3 ; KEY ALT+I, ” ON PAD convpad OF example ACTIVATE POPUP conversion ON PAD cardpad OF example ACTIVATE POPUP cardinfo DEFINE POPUP conversion MARGIN RELATIVE COLOR SCHEME 4 DEFINE BAR 1 OF conversion PROMPT ‘Ar\<ea’ ; KEY CTRL+E, ‘^E’ DEFINE BAR 2 OF conversion PROMPT ‘\<Length’ ; KEY CTRL+L, ‘^L’ DEFINE BAR 3 OF conversion PROMPT ‘Ma\<ss’ ; KEY CTRL+S, ‘^S’ DEFINE BAR 4 OF conversion PROMPT ‘Spee\<d’ ; KEY CTRL+D, ‘^D’ DEFINE BAR 5 OF conversion PROMPT ‘\<Temperature’ ; KEY CTRL+T, ‘^T’ DEFINE BAR 6 OF conversion PROMPT ‘T\<ime’ ; KEY CTRL+I, ‘^I’ DEFINE BAR 7 OF conversion PROMPT ‘Volu\<me’ ; KEY CTRL+M, ‘^M’ ON SELECTION POPUP conversion DO choice IN actimenu; WITH PROMPT( ), POPUP( ) DEFINE POPUP cardinfo MARGIN RELATIVE COLOR SCHEME 4 DEFINE BAR 1 OF cardinfo PROMPT ‘\<View Charges’ ; KEY ALT+V, ” DEFINE BAR 2 OF cardinfo PROMPT ‘View \<Payments’ ; KEY ALT+P, ” DEFINE BAR 3 OF cardinfo PROMPT ‘Vie\<w Users’ ; KEY ALT+W, ” DEFINE BAR 4 OF cardinfo PROMPT ‘\-‘ DEFINE BAR 5 OF cardinfo PROMPT ‘\<Charges ‘ ; KEY ALT+C, ” ON SELECTION POPUP cardinfo; DO choice IN actimenu WITH PROMPT( ), POPUP( ) ACTIVATE MENU example DEACTIVATE MENU example RELEASE MENU example EXTENDED SET SYSMENU TO DEFAULT ON KEY LABEL ESC PROCEDURE choice PARAMETERS mprompt, mpopup WAIT WINDOW ‘You chose ‘ + mprompt + ‘ from popup ‘ + mpopup NOWAIT

    See Also

    Concepts

    CLEAR Commands

    CREATE MENU Command

    DEACTIVATE MENU Command

    DEFINE MENU Command

    DEFINE PAD Command

    HIDE MENU Command

    SET SYSMENU Command

    SHOW MENU Command

    Commands (Visual FoxPro)

    Language Reference (Visual FoxPro)

    ACTIVATE POPUP Command

    Displays and activates a menu.

    ACTIVATE POPUP MenuName [AT nRow, nColumn] [BAR nMenuItemNumber] [NOWAIT] [REST]

    Parameters

    MenuName

    Specifies the name of the menu to activate. AT nRow, nColumn

    Specifies the position of the menu on the screen or in a user-defined window. The row and column coordinate applies to the upper-left corner of the menu. The position you specify with this argument takes precedence over a position you specify with the FROM argument in DEFINE POPUP. BAR nMenuItemNumber

    Specifies the item in the menu that is selected when the menu is activated. For example, if nMenuItemNumber is 2, the second item is selected. The first item is selected if you omit BAR nMenuItemNumber or if nMenuItemNumber is greater than the number of items in the menu. NOWAIT

    Specifies that, at run time, a program does not wait for the user to choose an item from the menu before continuing program execution. Instead, the program continues to execute. REST

    A menu created with the PROMPT FIELD clause of DEFINE POPUP places records from a field into the menu. When the menu is activated, the first item in the menu is initially selected, even if the record pointer in the table containing the field is positioned on a record other than the first record.

    Include REST to specify that the item selected when the menu is activated corresponds to the current record pointer position in the table.

    Remarks

    ACTIVATE POPUP works in conjunction with DEFINE POPUP, used to create the menu, and DEFINE BAR, used to create the items on the menu.

    Example

    This example uses ACTIVATE POPUP with ON PAD to activate a menu when a menu title is chosen. The current system menu bar is first saved to memory with SET SYSMENU SAVE, and then all system menu titles are removed with SET SYSMENU TO.

    Two new system menu titles are created with DEFINE PAD; DEFINE POPUP is used to create a menu for each menu title. DEFINE BAR is used to create menu items on each of the menus. When a menu title is chosen, ON PAD uses ACTIVATE POPUP to activate the corresponding menu.

    When an item is chosen from a menu, the CHOICE procedure is executed. CHOICE displays the name of the chosen item and the name of the menu containing the item. If the Exit item is chosen from the Card Info menu, the original Visual FoxPro system menu is restored.

     Copy Code
    *** Name this program ACTIPOP.PRG *** CLEAR SET SYSMENU SAVE SET SYSMENU TO DEFINE PAD convpad OF _MSYSMENU PROMPT ‘\<Conversions’ COLOR SCHEME 3 ; KEY ALT+C, ” DEFINE PAD cardpad OF _MSYSMENU PROMPT ‘Card \<Info’ COLOR SCHEME 3 ; KEY ALT+I, ” ON PAD convpad OF _MSYSMENU ACTIVATE POPUP conversion ON PAD cardpad OF _MSYSMENU ACTIVATE POPUP cardinfo DEFINE POPUP conversion MARGIN RELATIVE COLOR SCHEME 4 DEFINE BAR 1 OF conversion PROMPT ‘Ar\<ea’ KEY CTRL+E, ‘^E’ DEFINE BAR 2 OF conversion PROMPT ‘\<Length’ ; KEY CTRL+L, ‘^L’ DEFINE BAR 3 OF conversion PROMPT ‘Ma\<ss’ ; KEY CTRL+S, ‘^S’ DEFINE BAR 4 OF conversion PROMPT ‘Spee\<d’ ; KEY CTRL+D, ‘^D’ DEFINE BAR 5 OF conversion PROMPT ‘\<Temperature’ ; KEY CTRL+T, ‘^T’ DEFINE BAR 6 OF conversion PROMPT ‘T\<ime’ ; KEY CTRL+I, ‘^I’ DEFINE BAR 7 OF conversion PROMPT ‘Volu\<me’ ; KEY CTRL+M, ‘^M’ ON SELECTION POPUP conversion; DO choice IN actipop WITH PROMPT(), POPUP() DEFINE POPUP cardinfo MARGIN RELATIVE COLOR SCHEME 4 DEFINE BAR 1 OF cardinfo PROMPT ‘\<View Charges’ ; KEY ALT+V, ” DEFINE BAR 2 OF cardinfo PROMPT ‘View \<Payments’ ; KEY ALT+P, ” DEFINE BAR 3 OF cardinfo PROMPT ‘Vie\<w Users’ ; KEY ALT+W, ” DEFINE BAR 4 OF cardinfo PROMPT ‘\-‘ DEFINE BAR 5 OF cardinfo PROMPT ‘\<Charges’ ; KEY ALT+C, ” DEFINE BAR 6 OF cardinfo PROMPT ‘\-‘ DEFINE BAR 7 OF cardinfo PROMPT ‘E\<xit’; KEY ALT+X, ” ON SELECTION POPUP cardinfo; DO choice IN actipop WITH PROMPT(),POPUP() PROCEDURE choice PARAMETERS mprompt, mpopup WAIT WINDOW ‘You chose ‘ + mprompt + ; ‘ from popup ‘ + mpopup NOWAIT IF mprompt = ‘Exit’ SET SYSMENU TO DEFAULT ENDIF

    See Also

    Concepts

    CLEAR Commands

    CREATE MENU Command

    DEACTIVATE POPUP Command

    DEFINE BAR Command

    DEFINE POPUP Command

    HIDE POPUP Command

    MOVE POPUP Command

    ON SELECTION POPUP Command

    POP POPUP Command

    POPUP( ) Function

    PROMPT( ) Function

    PUSH POPUP Command

    SHOW POPUP Command

    Commands (Visual FoxPro)

    Language Reference (Visual FoxPro)

    ACTIVATE SCREEN Command

    Sends all subsequent output to the main Visual FoxPro window instead of to the active user-defined window.

    ACTIVATE SCREEN

    Remarks

    Use ACTIVATE WINDOW to direct output to a user-defined window.

    See Also

    Concepts

    ACTIVATE WINDOW Command

    DEACTIVATE WINDOW Command

    DEFINE WINDOW Command

    HIDE WINDOW Command

    SHOW WINDOW Command

    Commands (Visual FoxPro)

    Language Reference (Visual FoxPro)

    ACTIVATE WINDOW Command

    Displays and activates one or more user-defined windows or Visual FoxPro system windows.

    ACTIVATE WINDOW WindowName1 [, WindowName2 …] | ALL [IN [WINDOW] WindowName3 | IN SCREEN [BOTTOM | TOP | SAME] [NOSHOW]

    Parameters

    WindowName1[, WindowName2…]

    Specifies the name of each window to activate. Separate the window names with commas. In Visual FoxPro, you can specify the name of a toolbar to activate. See SHOW WINDOW Command for a list of Visual FoxPro toolbar names. ALL

    Specifies that all windows are activated. The last window activated is the active output window. IN [WINDOW] WindowName3

    Specifies the name of the parent window within which the window is placed and activated. The activated window becomes a child window. A parent window can have multiple child windows. A child window activated inside a parent window cannot be moved outside the parent window. If the parent window is moved, the child window moves with it.

    Note:
    The parent window must be visible for any of its child windows to be visible.

    IN SCREEN

    Places and activates a window in the main Visual FoxPro window. A window can be placed in a parent window by including IN WINDOW in DEFINE WINDOW when the window is created. Including the IN SCREEN clause in ACTIVATE WINDOW overrides the IN WINDOW clause in DEFINE WINDOW. BOTTOM | TOP | SAME

    Specifies where windows are activated with respect to other previously activated windows. By default, a window becomes the window on top when it is activated. Including BOTTOM places a window behind all other windows. TOP places it in front of all other windows. SAME activates a window without affecting its front-to-back placement. NOSHOW

    Activates and directs output to a window without displaying the window.

    Remarks

    To successfully use this command on user-defined windows, any target user-defined window must have been created using the DEFINE WINDOW Command command.

    Activating a window makes it the window on top and directs all output to that window. Output can be directed to only one window at a time. A window remains the active output window until it is deactivated or released, or until another window or the main Visual FoxPro window is activated.

    The names of user-defined windows appear in the bottom section of the Window menu. The name of the active user-defined window is marked with a check mark.

    More than one window can be placed in the main Visual FoxPro window at one time, but output is directed only to the last window activated. When more than one window is open, deactivating the active output window removes it from the main Visual FoxPro window and sends subsequent output to another window. If there is no active output window, output is directed to the main Visual FoxPro window.

    Note:
    To ensure output is directed to a specific window when you deactivate the active output window, you must explicitly activate the window you want to send output to with ACTIVATE WINDOW.

    All activated windows are displayed until DEACTIVATE WINDOW or HIDE WINDOW is issued to remove them from view. Issuing either command removes windows from view but not from memory. Windows can be redisplayed by issuing ACTIVATE WINDOW or SHOW WINDOW.

    To remove windows from view and from memory, use CLEAR WINDOWS, RELEASE WINDOWS, or CLEAR ALL. Windows that are removed from memory must be redefined to place them back in the main Visual FoxPro window.

    You can use ACTIVATE WINDOW to place Visual FoxPro system windows in the main Visual FoxPro window or in a parent window.

    The following system windows can be opened with ACTIVATE WINDOW:

    • Command
    • Call Stack
    • Debug
    • Debug Output
    • Document View
    • Locals
    • Trace
    • Watch
    • View

    To activate a system window and or a toolbar, enclose the entire system window or toolbar name in quotation marks. For example, to activate the Call Stack debugging window in Visual FoxPro, issue the following command:

     Copy Code
    ACTIVATE WINDOW “Call Stack”

    Historically in prior versions of Visual FoxPro, the Data Session window has always been referred to as the View window. Additionally, language used to control this window, such as HIDE WINDOW, ACTIVATE WINDOW, WONTOP( ), also refers to this window as the View window. Visual FoxPro continues to refer to the View window for the ACTIVATE WINDOW command.

    Use HIDE WINDOW or RELEASE WINDOW to remove a system window from the main Visual FoxPro window or a parent window.

    Example

    The following example defines a window named output and activates it, placing it in the main Visual FoxPro window. The WAIT command pauses execution, the window is hidden, and then redisplayed.

     Copy Code
    CLEAR DEFINE WINDOW output FROM 2,1 TO 13,75 TITLE ‘Output’ ; CLOSE FLOAT GROW ZOOM ACTIVATE WINDOW output WAIT WINDOW ‘Press any key to hide window output’ HIDE WINDOW output WAIT WINDOW ‘Press any key to show window output’ SHOW WINDOW output WAIT WINDOW ‘Press any key to release window output’ RELEASE WINDOW output

    See Also

    Concepts

    CLEAR Commands

    DEACTIVATE WINDOW Command

    DEFINE WINDOW Command

    HIDE WINDOW Command

    RELEASE WINDOWS Command

    SHOW WINDOW Command

    Commands (Visual FoxPro)

    Language Reference (Visual FoxPro)

    Microsoft Visual FoxPro 9.0

    • Article
    • 08/29/2016

    Microsoft® Visual FoxPro® database development system is a powerful tool for quickly creating high-performance desktop, rich client, distributed client, client/server, and Web database applications. Employ its powerful data engine to manage large volumes of data, its object-oriented programming to reuse components across applications, its XML Web services features for distributed applications, and its built-in XML support to quickly manipulate data.

    Note that Visual FoxPro 9.0 is the last version and was published in 2007.


    Download Visual FoxPro 9.0 SP2

    Download Service Pack 2 for Microsoft Visual FoxPro 9.0. SP2 provides the latest updates to Visual FoxPro 9.0 combining various enhancements and stability improvements into one integrated package.

    Three Hotfixes for Visual FoxPro 9.0 SP2

    Visual FoxPro Samples and Updates

    Find code samples and product updates for Visual FoxPro.

    Visual FoxPro on MSDN Forums

    Join the conversation and get your questions answered on the Visual FoxPro Forum on MSDN.

    Visual FoxPro 9.0 Overview

    With its local cursor engine, tight coupling between language and data, and powerful features, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications for the desktop, client-server environments, or the Web. Developers will have the necessary tools to manage data—from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully-developed data management application for end users.

    • Data-Handling and Interoperability. Create .NET compatible solutions with hierarchical XML and XML Web services. Exchange data with SQL Server through enhanced SQL language capabilities and newly supported data types.
    • Extensible Developer Productivity Tools. Enhance your user interfaces with dockable user forms, auto-anchoring of controls, and improved image support. Personalize the Properties Window with your favorite properties, custom editors, fonts, and color settings.
    • Flexibility to Build All Types of Database Solutions. Build and deploy stand-alone and remote applications for Windows based Tablet PCs. Create and access COM components and XML Web Services compatible with Microsoft .NET technology.
    • Reporting System Features. Extensible new output architecture provides precision control of report data output and formatting. Design with multiple detail banding, text rotation, and report chaining. Output reports supported include in XML, HTML, image formats, and customizable multi-page print preview window. Backward compatible with existing Visual FoxPro reports.

    Resources

    Visual FoxPro Downloads

    • Article
    • 08/29/2016

    Download samples, along with the final product updates including service packs for Visual FoxPro to ensure maximum productivity and performance from your Visual FoxPro development.

    Visual FoxPro 9.0 Updates

    • Visual FoxPro 9.0 Service Pack 2 (SP2)
      Download Service Pack 2 for Microsoft Visual FoxPro 9.0. SP2 provides the latest updates to Visual FoxPro 9.0 combining various enhancements and stability improvements into one integrated package.
    • Help Download for Visual FoxPro 9.0 SP2
      Download product documentation for Visual FoxPro 9.0 SP2.
    • GDI+ Update for Visual FoxPro 9.0 SP2
      Security update patch for Visual FoxPro 9.0 SP2 for fixing Buffer Overrun in JPEG Processing (GDI+).
    • GDI+ Update for Visual FoxPro 9.0 SP1
      Security update patch for Visual FoxPro 9.0 SP1 for fixing Buffer Overrun in JPEG Processing (GDI+). Note: We highly recommend that you install Service Pack 2, then apply the GDI+ SP2 update.
    • Visual FoxPro 9.0 ‘Sedna’ AddOns
      AddOn pack for Visual FoxPro 9.0. This download contains six components: VistaDialogs4COM, Upsizing Wizard, Data Explorer, NET4COM, MY for VFP and VS 2005 Extension for VFP.
    • XSource for Visual FoxPro 9.0 SP2
      Download XSource for Visual FoxPro 9.0 SP2. XSource.zip has its own license agreement for usage, modification, and distribution of the Xbase source files included.
    • Microsoft OLE DB Provider for Visual FoxPro 9.0 SP2
      The Visual FoxPro OLE DB Provider (VfpOleDB.dll) exposes OLE DB interfaces that you can use to access Visual FoxPro databases and tables from other programming languages and applications. The Visual FoxPro OLE DB Provider is supported by OLE DB System Components as provided by MDAC 2.6 or later. The requirements to run the Visual FoxPro OLE DB Provider are the same as for Visual FoxPro 9.0. Note: This version of the VFP OLE DB provider is the same version as the one included with Visual FoxPro 9.0 SP2.
    • VFPCOM Utility
      Extend Visual FoxPro interoperability with other COM and ADO components with the VFPCOM Utility. This utility is a COM server that provides additional functionality when you use ADO and access COM events with your Visual FoxPro 9.0 applications. For installation instructions and more details on the issues that have been addressed, consult the VFPCOM Utility readme.
    • Visual FoxPro ODBC Driver
      The VFPODBC driver is no longer supported. We strongly recommend using the Visual FoxPro OLE DB provider as a replacement. Please refer to the following article for more information and related links to issues when using the VFPODBC driver: https://support.microsoft.com/kb/277772.

    Visual FoxPro 8.0 Updates

    • Visual FoxPro 8.0 Service Pack 1Download Microsoft Visual FoxPro 8.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 8.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.
    • GDI+ Update for Visual FoxPro 8.0 SP1Security update patch for Visual FoxPro 8.0 SP1 for fixing Buffer Overrun in JPEG Processing (GDI+).
    • Visual FoxPro 8.0 SP1 Task Pane Source CodeSource code for Task Pane Manager component included in SP1 for Visual FoxPro 8.0. SP1 for VFP 8.0 included an updated Task Pane Manager component as an .APP application file but did not contain the update source code files associated with the updated version.
    • Visual FoxPro 8.0 Localization Toolkit OverviewOverview document of the Localization Toolkit project results for making available various language versions of the design-time IDE DLL and help documentation as add-ons to the English version of Visual FoxPro 8.0.

    Visual FoxPro 7.0 Updates

    • Visual FoxPro 7.0 Service Pack 1Download Microsoft Visual FoxPro 7.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 7.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.

    Code Samples

    • .NET Samples for Visual FoxPro DevelopersThis download contains different projects and source files which are designed to show how how some common Visual FoxPro functionally is created in Visual Basic .NET.
    • Visual FoxPro 8.0 SamplesThis download contains different projects which are designed to show how new features in Visual FoxPro 8.0 can be used. Each project is self-contained and can be run independently of any other. There is a readme text file contained in each project that describes each sample program.
    • Sample: Visual FoxPro DDEX Provider for Visual Studio 2005A Data Designer EXtension Provider allows a data source to integrate better with data tools in Visual Studio. Visual FoxPro “Sedna” included a sample for such a provider for VFP data.This is now available as a stand-alone download.

    System Requirements

    • Article
    • 08/29/2016

    To install Microsoft Visual FoxPro 9.0, you need:Expand table

    Minimum Requirements
    ProcessorPC with a Pentium-class processor
    Operating SystemMicrosoft Windows 2000 with Service Pack 3 or later operating systemMicrosoft Windows XP or laterMicrosoft Windows Server 2003 or later
    Memory64 MB of RAM minimum; 128 MB or higher recommended
    Hard Disk165 MB of available hard-disk space for typical installation; 20 MB of additional hard-disk space for Microsoft Visual FoxPro 9.0 Prerequisites
    DriveCD-ROM or DVD-ROM drive
    DisplaySuper VGA 800 X 600 or higher-resolution monitor with 256 colors
    MouseMicrosoft Mouse or compatible pointing device

    Frequently Asked Questions

    • Article
    • 08/29/2016

    Find answers to your frequently asked questions about Visual FoxPro.

    Q: What operating system is required for Visual FoxPro 9.0?

    Developing applications with Visual FoxPro 9.0 is supported only on Microsoft Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. You can create and distribute run-time applications for Windows 98, Windows Me, Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. Installation on Windows NT 4.0 Terminal Server Edition is not supported.

    Q: Will there be a Visual FoxPro 10.0?

    No. There will not be another major release of Visual FoxPro (see announcement: A message to the community, March 2007).

    Q: Will there be updates to Visual FoxPro?

    Yes. Visual FoxPro will continue to be supported as per the lifecyle policy (https://support.microsoft.com/lifecycle/?p1=7992). Visual FoxPro 9 will be supported until 2014. In support of these products we may release patch updates from time to time. These typically fix problems discovered either internally or by a customer and reported to our product support engineers.

    Q: Will there be a service pack 3 for Visual FoxPro 9?

    At this time there are no plans to release a service pack for Visual FoxPro. However if there arises a need to publish a collection of fixes we may release a service pack. We will make announcements on the Visual FoxPro home page.

    Q: What types of applications can I build with Visual FoxPro 9.0?

    With its local cursor engine, tight coupling between language and data, and powerful features, such as object-oriented programming, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes, from desktop and client/server database applications to data-intensive COM components and XML Web services.

    Visual FoxPro 9.0 is an application development tool for building extremely powerful database applications and components. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications on the desktop, client/server, or on the Web, through components and XML Web services. Developers will have the necessary tools to manage data from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully developed data management application for end users.

    Q: Can I use Visual FoxPro to build Web applications?

    Visual FoxPro COM components can be used with Internet Information Services (IIS) to build high-powered Internet database applications. This is because Visual FoxPro components can be called from Active Server Pages (ASP). Visual FoxPro is compatible with ASP but works even better in conjunction with the more modern ASP.NET. The components will retrieve and manipulate data, and will build some of the HTML returned to the user.

    Q: Can you consume XML Web services with Visual FoxPro?

    Yes, Visual FoxPro 9.0 makes it easy to consume XML Web services by integrating the SOAP Toolkit into the product.

    Q: Is Visual FoxPro a part of MSDN Subscriptions?

    Yes, Visual FoxPro 9.0 is included in the Professional, Enterprise, and Universal levels of MSDN Subscriptions. Visual FoxPro 9.0 is available for download to MSDN Subscribers via MSDN Subscriber downloads.

    Q: How long will Visual FoxPro be supported by Microsoft?

    Visual FoxPro 9.0 has standard support by Microsoft through January 2010 and extended support through January 2015 as per the developer tools lifecycle support policy.

    Q: How long will the SOAP Toolkit included in Visual FoxPro 9.0 be supported by Microsoft?

    Licensed users of Visual FoxPro 9.0 have a special lifecycle support plan for the SOAP Toolkit, supported by Microsoft on the same support plan as Visual FoxPro 8.0 which is through April 2008 and extended support through September 2013.

    Q: Is Visual FoxPro 9.0 compatible with Visual Studio 2005 and SQL Server 2005?

    Yes. We improved XML support and added new data types in Visual FoxPro 9.0 which improves .NET interop and SQL Server compatibility. Moreover the ‘Sedna’ add-on pack includes improvements to the Data Explorer and the Upsizing Wizard. These have significant improvements to support SQL Server 2005.

    Q: How does Visual FoxPro 9.0 compare to SQL Server?

    We do not contrast Visual FoxPro versus SQL Server. We position SQL Server as a database engine and Visual FoxPro as a developer tool. While Visual FoxPro has a database engine built-in, it is not positioned as a stand-alone database engine only. The trend is for an increasing amount of Visual FoxPro based applications to use SQL Server as the data storage in the solution. Of course, this is not required; it depends on the requirements of the solution. SQL Server offers security, reliability, replication, and many other features of a full relational database engine while the Visual FoxPro database system is an open file based DBF system that does not have many of those features. We leave it up to developers and companies to position and to compare various Microsoft products and technologies with each other and decide which ones are best for them to use when and how.

    Q: Are there plans to enhance the 2 GB database size limit in Visual FoxPro?

    The 2 GB limit is per table, not per database. We do not have any plans to extend the 2 GB table size limit in Visual FoxPro due to many reasons including the 32-bit architecture that already exists within the product. For large, scalable databases we recommend SQL Server 2008.

    Q: Is Visual FoxPro supported on Windows Vista?

    Yes. Visual FoxPro 9 Service Pack 2 is fully supported on Windows Vista.

    Q: Are there plans for Visual FoxPro to support 64-bit versions of the Windows operating system?

    No. While Visual FoxPro will remain 32-bit and not natively use 64-bit addressing; it will run in 32-bit compatibility mode. Visual Studio 2008 supports creating native 64-bit applications.

    Q: How do you position Visual FoxPro in relation to Microsoft Access?

    Microsoft Access, the database in Office, is the most broadly used and easiest-to-learn database tool that Microsoft offers. If you are new to databases, if you are building applications that take advantage of Microsoft Office, or if you want an interactive product with plenty of convenience, then choose Microsoft Access. Visual FoxPro is a powerful rapid application development (RAD) tool for creating relational database applications. If you are a database developer who builds applications for a living and you want ultimate speed and power, then choose Visual FoxPro.

    Q: Is Visual FoxPro part of Visual Studio .NET?

    No. Visual FoxPro 9.0 is a stand-alone database development tool which is compatible and evolutionary from previous versions of Visual FoxPro. Visual FoxPro 9.0 does not use or install the Windows .NET Framework. Visual FoxPro 9.0 is compatible with Visual Studio .NET the area of XML Web services, XML support, VFP OLE DB provider, and more. Visual FoxPro and Visual Studio are complimentary tools that work great together, such as Visual FoxPro 9.0 plus ASP.NET for adding WebForm front ends and mobile device front ends to Visual FoxPro applications.

    Q: What is Microsoft’s position on Visual FoxPro related to Visual Studio and .NET?

    We do not have plans to merge Visual FoxPro into Visual Studio and .NET, and there are no plans to create any sort of new Visual FoxPro .NET language. Instead, we are working on adding many of the great features found in Visual FoxPro into upcoming versions of Visual Studio, just like we’ve added great Visual Studio features into Visual FoxPro. If you want to do .NET programming, you should choose a .NET language with Visual Studio.

    A Message to the Community

    • Article
    • 08/29/2016

    March 2007

    We have been asked about our plans for a new version of VFP. We are announcing today that there will be no VFP 10. VFP9 will continue to be supported according to our existing policy with support through 2015 (https://support.microsoft.com/lifecycle/?p1=7992). We will be releasing SP2 for Visual FoxPro 9 this summer as planned, providing fixes and additional support for Windows Vista.

    Additionally, as you know, we’ve been working on a project codenamed Sedna for the past year or so. Sedna is built using the extensibility model of VFP9 and provides a number of new features including enhanced connectivity to SQL Server, integration with parts of the .NET framework, support for search using Windows Desktop Search and Windows Vista as well as enhanced access to VFP data from Visual Studio.

    Concurrently, the community has been using CodePlex (https://www.codeplex.com) to enhance VFP using these same capabilities in the VFPx project. Some of these community driven enhancements include:

    • Support for GDI+
    • An enhanced class browser
    • Support for Windows Desktop Alerts
    • An object oriented menu system
    • Integration with MSBuild
    • A rule-based code analysis tool similar to fxCop in Visual Studio
    • An Outlook Control Bar control

    To reiterate, today we are announcing that we are not planning on releasing a VFP 10 and will be releasing the completed Sedna work on CodePlex at no charge. The components written as part of Sedna will be placed in the community for further enhancement as part of our shared source initiative. You can expect to see the Sedna code on CodePlex sometime before the end of summer 2007.

    Microsoft Visual FoxPro 9.0 SP2

    • Article
    • 07/09/2007

    In this article

    1. In the Visual FoxPro Documentation
    2. Additional Information

    Welcome to Microsoft Visual FoxPro. Visual FoxPro is the object-oriented relational database management system that makes it possible for you to create database solutions for the desktop to the Web. Visual FoxPro provides powerful data handling capabilities, rapid application development tools for maximum productivity, and the flexibility needed to build all types of database solutions.

    In the Visual FoxPro Documentation

    • What’s New in Visual FoxPro
      Describes the new features and enhancements included in this version of Visual FoxPro.
    • Getting Started with Visual FoxPro
      Provides information about where to find the Readme file, installing and upgrading from previous versions, configuring Visual FoxPro, and customizing the development environment.
    • Using Visual FoxPro
      Provides an overview of Visual FoxPro features, describes concepts and productivity tools for developing, programming, and managing high-performance database applications and components.
    • Samples and Walkthroughs
      Contains Visual FoxPro code samples and step-by-step walkthroughs that you can use for experimenting with and learning Visual FoxPro features.
    • Reference
      Includes Visual FoxPro general, programming language, user interface, and error message reference topics.
    • Product Support
      Provides information about Microsoft product support services for Visual FoxPro.

    Additional Information

    What’s New in Visual FoxPro

    • Article
    • 07/09/2007

    This release of Visual FoxPro contains many new features and enhancements. The following sections describe these new features and enhancements.

    In This Section

    Related Sections

    • Getting Started with Visual FoxPro
      Provides information about where to find the ReadMe file and how to install and upgrade from previous versions, configure Visual FoxPro, and customize the development environment.
    • Using Visual FoxPro
      Provides an overview of Visual FoxPro features, describes concepts and productivity tools for developing, programming, and managing high-performance database applications and components, and provides walkthroughs that help get you started. With the robust tools and data-centric object-oriented language that Visual FoxPro offers, you can build modern, scalable, multi-tier applications that integrate client/server computing and the Internet.
    • Samples and Walkthroughs
      Contains Visual FoxPro code samples and step-by-step walkthroughs that you can use for experimenting with and learning Visual FoxPro features.
    • Reference (Visual FoxPro)
      Describes Visual FoxPro general, programming language, user interface, and error message reference topics.
    • Product Support (Visual FoxPro)
      Provides information about Microsoft product support services for Visual FoxPro.

    English (United States)

    Your Privacy ChoicesTheme

    Guide to Reporting Improvements

    • Article
    • 07/09/2007

    In this article

    1. Design-time Enhancements
    2. Multiple Detail Bands
    3. Object-assisted Run-time Report Processing
    4. Printing, Rendering, and Character-set-handling Improvements

    Show 2 more

    Visual FoxPro 9’s Report System has undergone a thorough revision. This topic sketches the broad outlines of the changes, and provides you with information about where to look for details.

    The following main areas of enhancements to the Report System are covered in sections of this topic.

    • Design-time enhancements.
      Multiple features and changes make designing reports in Visual FoxPro better for you and your end-users. The Report Builder Application re-organizes your design experience out-of-the-box. If you want to customize the design process, Report Builder dialog boxes and Report Designer events are fully exposed for you to do so.
    • Multiple detail bands.
      You can handle multiple child tables and data relationships more flexibly in the revised Report Designer. When you run multiple-detail-band reports, you can leverage the new bands, with associated detail headers and footers, both for appropriate presentation of these relationships and for more capable calculations.
    • Object-assisted run-time report processing.
      An entirely re-built output system, including a new base class, changes the way Visual FoxPro provides output report and label files at run time. Object-assisted reporting provides better-quality output, new types of output, and an open-architecture based on a new Visual FoxPro base class, the ReportListener. A programmable Report Preview interface interacts with ReportListeners to give you full control over report preview experience. The Report Preview Application provides improved out-of-the-box previewing facilities.
    • Printing, rendering, and character-set-handling improvements.
      Visual FoxPro 9 makes better use of the operating system’s printing features and GDI+ rendering subsystem. It also handles multiple locales and character sets better than previous versions. These changes are showcased in the Report System, and are accessible for use in custom code during report design and run-time processing.
    • Extensible use of report and label definition files (.frx and .lbx tables).
      Visual FoxPro 9 handles your existing reports and labels without modification, while allowing you to add new features and behavior to these reports easily. This backward-compatible, yet forward-thinking, migration strategy is made possible by the Report System’s newly-flexible handling of the .frx and .lbx table structure.

    Design-time Enhancements

    Numerous changes in the Report System help you enhance the design-time experience for developers and end-users. This section directs you to information about design-time improvements.

    Report Designer Event Hooks and the Report Builder Application

    The Report Designer now offers Report Builder Hooks, which enable you to intercept events occurring during a report or label design session to override and extend designer activity. The default Report Builder Application replaces many of the standard reporting dialog boxes with new ones written in Visual FoxPro code. Components of the Report Builder Application are exposed as Visual FoxPro Foundation Classes for your use.Expand table

    To learn about:Read:
    Report Builder HooksUnderstanding Report Builder Events
    How the Report Builder Application uses Report Builder HooksHow to: Configure the Report Builder’s Event Handling
    How to specify and distribute a Report Builder with your applications_REPORTBUILDER System VariableHow to: Specify and Distribute ReportBuilder.AppIncluding Report Files for Distribution
    Using Report Builder algorithms in your codeFRX Cursor Foundation ClassFRX Device Helper Foundation Class

    Protection for End-User Design Sessions, and other Design-time Customization Opportunities

    You can allow end-users to MODIFY and CREATE reports and labels, while setting limitations on what they can do in the Report Designer interface, using the new PROTECTED keyword. Protection is available individually by object and globally for the report. You can change what end-users see on the designer layout surface, from complex expressions to simple labels or sample data, while working in PROTECTED design mode, using Design-Time Captions. You can also provide helpful instructions, for both PROTECTED and standard design mode, by specifying Tooltips for report controls.Expand table

    To learn about:Read:
    Using the PROTECTED keywordMODIFY REPORT CommandMODIFY LABEL Command
    Setting Protection in the Report or Label Designer, and what Protection settings doSetting Protection for Reports
    Protection settings exposed in Report or Label Dialog dialog boxes when you use the default Report Builder ApplicationProtection Tab, Report Control Properties Dialog Box (Report Builder)Protection Tab, Report Properties Dialog Box (Report Builder)Protection Tab, Report Band Properties Dialog Box (Report Builder)
    Design-Time CaptionsHow to: Add Design-time Captions to Field Controls
    ToolTips for Report ControlsHow to: Add Tooltips to Report Controls

    Enhanced Data Environment Use in Reports

    You can save the Data Environment you designed for a Report or Label as a visual class. You can load a Data Environment into a Report or Label design from either a visual class or a previously-saved report or label.Expand table

    To learn about:Read:
    Saving a Report Data EnvironmentHow to: Save Report Data Environments as Classes
    Loading a Report Data EnvironmentData Environment Tab, Report Properties Dialog Box (Report Builder)How to: Load Data Environments for Reports

    Miscellaneous Design Improvements

    There have been numerous enhancements to the Report and Label Designers. Some features are subtle changes to make design sessions more efficient and more enjoyable, and others improve your choices for resulting output.Expand table

    To learn about:Read:
    Improvements to the Report and Label Interactive Development Environment (IDE), such as:Enhanced Report Designer toolbar, and easier access to the Report Designer Toolbar from the View menuNew global Report Properties context menuImprovements and additions to existing context menusRevised and extended Report menuReport Layout and Design
    Changes to global report and label design optionsReports Tab, Options Dialog Box
    Using the new PictureVal property of the Image control to specify images in reportsHow to: Add Pictures to ReportsPictureVal Property
    New picture template characters (U and W) and updated format instructions (Z, now supported for date and datetime data), useful in reports and labelsFormat Expressions for Field ControlsInputMask PropertyFormat Property
    Receiving improved HTML output, which leverages run-time reporting enhancements, when you choose Save As HTML… while designing a report or labelHow to: Generate Output for Reports TipOther Visual FoxPro components that invoke Genhtml.prg, the default _GENHTML implementation, automatically share the improved HTML output, although these components have not changed. These include the FRX to HTML Foundation Class and the Output Object Foundation Class.
    Report document properties enable you to include information about the report in the report. Document properties are included as elements and attributes in XML and HTML output.How to: Add Document Properties to a ReportDocument Properties Tab, Report Properties Dialog Box (Report Builder)
    You can dynamically change the properties of report controls at run time based on the evaluation of an expression.How to: Dynamically Format Report ControlsDynamics Tab, Report Control Properties Dialog Box (Report Builder)

    Multiple Detail Bands

    The Report Engine can now move through a scope of records multiple times. The records can represent related sets of detail lines in child tables, or they can be multiple passes through a single table. These multiple passes through a scope of records are represented as multiple detail bands.

    Detail bands can have their own headers and footers, their own associated onEntry and onExit code, and their own associated report variables. Each detail band can be explicitly associated with a separate target alias, allowing you to control the number of entries in each detail band separately for related tables.

    Multiple detail band reports provide many new ways you can represent data in reports and labels, and new ways you can calculate or summarize data, as you move through a record scope.Expand table

    To learn about:Read:
    Designing reports and labels with multiple detail bands and their associated headers and footersOptional Bands Dialog BoxReport Band Properties Dialog BoxBand Tab, Report Band Properties Dialog Box (Report Builder)
    Handling multiple, related tables in report and label dataControlling Data in ReportsWorking with Related Tables using Multiple Detail Bands in Reports
    Associating report variables with detail bandsHow to: Reset Report Variables
    Comparing multiple groups and multiple detail bandsReport Bands

    Object-assisted Run-time Report Processing

    Visual FoxPro 9 has a new, object-assisted method of generating output from reports and labels. You can use your existing report and label layouts in object-assisted mode, to:

    • Generate multiple types of output during one report run.
    • Connect multiple reports together as part of one output result.
    • Improve the quality of traditional report output.
    • Dynamically adjust the contents of a report while you process it.
    • Provide new types of output not available from earlier versions of Visual FoxPro.

    This section covers the array of run-time enhancements that work together to support object-assisted reporting mode.

    Object-Assisted Architecture and ReportListener Base Class

    The new ReportListener base class and supporting language enhancements are the heart of run-time reporting enhancements.Expand table

    To learn about:Read:
    Fundamentals of the architecture, how its components work together, and what happens during an object-assisted report runUnderstanding Visual FoxPro Object-Assisted Reporting
    The ReportListener base class and its membersReportListener ObjectReportListener Object Properties, Methods, and Events
    Invoking object-assisted reporting mode automaticallySET REPORTBEHAVIOR Command_REPORTOUTPUT System VariableReports Tab, Options Dialog Box
    Invoking object-assisted reporting mode explicitly with Visual FoxPro commandsREPORT FORM CommandLABEL Command
    Debugging and error-handling object-assisted report runsHandling Errors During Report Runs

    Report Preview API and the Report Preview Application

    Visual FoxPro 9’s object-assisted reporting mode gives you complete control over report and label previews.Expand table

    To learn about:Read:
    How object-assisted preview worksThe Preview Container APICreating a Custom Preview Container
    The default Report Preview ApplicationLeveraging the Default Preview Container
    How to specify and distribute Report Preview components with your applications_REPORTPREVIEW System VariableHow to: Specify and Distribute ReportPreview.AppIncluding Report Files for Distribution

    New Types of Output and the Report Output Component Set

    Because you can subclass ReportListener, you can create new types of output. Visual FoxPro 9 supplies a Report Output Application to connect ReportListener subclasses with output types, as well as ReportListener-derived classes with enhanced output capabilities.Expand table

    To learn about:Read:
    Requirements for Report Output Application, and how Visual FoxPro uses Report Output Applications_REPORTOUTPUT System Variable
    Features of the default Report Output ApplicationUnderstanding the Report Output Application
    Specifying custom output handlers using the default Report Output ApplicationHow to: Specify an Alternate Report Output Registry TableHow to: Register Custom ReportListeners and Custom OutputTypes in the Report Output Registry TableConsiderations for Creating New Report Output Types
    Understanding and configuring the Visual FoxPro Foundation Classes providing default ReportListener behavior for object-assisted preview and printingReportListener User Feedback Foundation Class
    Understanding and configuring the Visual FoxPro Foundation Classes responsible for default XML and HTML outputReportListener XML Foundation ClassReportListener HTML Foundation Class
    Leveraging the full set of supported Report Output Foundation Classes and VFP Report Output XML formatReportListener Foundation ClassesUsing VFP Report Output XML
    How to specify and distribute Report Output components with your applicationsHow to: Specify and Distribute Report Output Application ComponentsIncluding Report Files for Distribution

    Migration Strategies and Changes in Output Rendering

    You can use the design-time changes to improve all reports and labels, whether you choose backward-compatible or object-assisted reporting mode at run time.

    When evaluating whether to switch to object-assisted reporting mode at run time, first consider items on the Reporting list of Important Changes in the Changes in Functionality for the Current Release topic, some of which are specific to this new method of creating output. .The topic includes a table of minor differences between backward-compatible and object-assisted reporting output. You can examine what effects these changes might have on individual existing reports, and use the recommendations in the table to address them. You will find additional details in the topic Using GDI+ in Reports.

    Once you have experimented with your current reports, you can decide on a migration strategy for output:

    • You can switch applications over to use object-assisted reporting mode completely, by using the command SET REPORTBEHAVIOR 90.
    • You can use SET REPORTBEHAVIOR 90 but preface specific REPORT FORM commands for reports with formatting issues with SET REPORTBEHAVIOR 80, returning your application to object-assisted mode afterwards.
    • You can use object-assisted mode all the time, but adjust your ReportListener-derived classes’ behavior to suit specific needs. For example, you could change the default setting of the ReportListener’s DynamicLineHeight Property to False (.F.).
    • You can leave SET REPORTBEHAVIOR at its default setting of 80, and add an explicit OBJECT clause to specific reports at your leisure, as you have the opportunity to evaluate and adjust individual report and label layouts.

    Printing, Rendering, and Character-set-handling Improvements

    General changes to Visual FoxPro’s use of Windows’ printing, rendering and font-handling support the improvements in the Report System’s output. These changes enhance your ability to support multiple printers and multiple languages in reports.Expand table

    To learn about:Read:
    GDI+ features and their impact on native Visual FoxPro outputUsing GDI+ in Reports
    Visual FoxPro reporting enhancements that allow your code to use GDI+ in object-assisted reporting mode, and Visual FoxPro Foundation Classes to get you startedGDIPlusGraphics PropertyRender MethodGDI Plus API Wrapper Foundation Classes
    Making full use of multiple character sets, or language scripts, in reports, for single report layout elements, for report defaults, or globally in Visual FoxProGETFONT( ) FunctionStyle Tab, Report Control Properties Dialog Box (Report Builder)How to: Change Page Settings for ReportsReports Tab, Options Dialog BoxReporting Features for International Applications
    Changes to page setup dialog boxes in Visual FoxPro, improvements in your programmatic access to them, and providing overrides to Printer Environment settings in report and label filesSYS(1037) – Page Setup Dialog Box
    Receiving improved information about the user’s installed printersAPRINTERS( ) Function
    Limiting a list of fonts to those appropriate for printer userGETFONT( ) Function

    Extensible Use of Report and Label Definition Files

    Underneath all the changes to the Visual FoxPro Report System, the Report Designer and Report Engine handle your report and label definitions using the same .frx and .lbx file structures as they did in previous versions. They change the way they use certain fields, without making these reports and labels invalid in previous versions, and they also allow you to extend your use of existing fields or add custom fields.

     Tip

    This change is critical to your ability to create extensions of the new reporting features. For example, you might store two sets of ToolTips in two report extension fields, one set for use by developers and one for use by end-users. In a Report Builder extension, you could evaluate whether the Designer was working in protected or standard mode, and replace the actual set of ToolTips from the appropriate extension field. In previous versions, you could not add fields to report or label structure; the Designer and Engine would consider the table invalid. You also could not add custom content to unused, standard fields in various report and label records safely, because the Report Designer removed such content.

    Visual FoxPro 9 provides a revised FILESPEC table for report and label files, with extensive information on the use of each column in earlier versions as well as current enhancements.

    Visual FoxPro 9 also establishes a new, structured metadata format for use with reports. This format is an XML document schema shared with the Class Designer’s XML MemberData.

    The XML document format allows you to pack custom reporting information into a single report or label field. The default Report Builder Application makes it easy to add Report XML MemberData to report and label records.Expand table

    To learn about:Read:
    How Visual FoxPro uses .frx and .lbx tables, and how to extend these structuresUnderstanding and Extending Report Structure
    How to find and display the contents of the revised FILESPEC table, 60FRX.dbfTable Structures of Table Files (.dbc, .frx, .lbx, .mnx, .pjx, .scx, .vcx)
    How you can edit the XML data using the Report Builder ApplicationHow to: Assign Structured Metadata to Report Controls
    How you can use Report XML MemberDataReport XML MemberData Extensions
    The shared MemberData document schemaMemberData Extensibility

    See Also

    Reference

    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Other Resources

    What’s New in Visual FoxPro

    Data and XML Feature Enhancements

    • Article
    • 07/09/2007

    In this article

    1. Extended SQL Capabilities
    2. New Data Types
    3. Binary Index Tag Based on Logical Expressions
    4. Converting Data Types with the CAST( ) Function

    Show 47 more

    Visual FoxPro contains the following additions and improvements to its data features:

    Extended SQL Capabilities

    Visual FoxPro contains many enhancements for SQL capabilities. For more information, see SQL Language Improvements.

    New Data Types

    Visual FoxPro includes the following new field and data types:

    • **Varchar **To store alphanumeric text without including padding by additional spaces at the end of the field or truncating trailing spaces, use the new Varchar field type.If you do not want Varchar fields translated across code pages, use the Varchar (Binary) field type. For more information, see Varchar Field Type.You can specify Varchar type mapping between ODBC, ADO, and XML data source types and CursorAdapter and XMLAdapter objects using the MapVarchar Property. You can also specify Varchar mapping for SQL pass-through technology and remote views using the MapVarchar setting in the CURSORSETPROP( ) function. For more information, see CURSORSETPROP( ) Function and CURSORGETPROP( ) Function.
    • **Varbinary **To store binary values and literals of fixed length in fields and variables without padding the field with additional zero (0) bytes or truncating any trailing zero bytes that are entered by the user, use the Varbinary data type. Internally, Visual FoxPro binary literals contain a prefix, 0h, followed by a string of hexadecimal numbers and are not enclosed with quotation marks (“”), unlike character strings. For more information, see Varbinary Data Type.You can specify binary type mapping between ODBC, ADO, and XML data source types and CursorAdapter and XMLAdapter objects using the MapBinary Property. You can also specify binary mapping for SQL pass-through technology and remote views using the MapBinary setting in the CURSORSETPROP( ) function. For more information, see CURSORSETPROP( ) Function and CURSORGETPROP( ) Function.
    • BlobTo store binary data with indeterminate length, use the Blob data type. For more information, see Blob Data Type.

    Many of the Visual FoxPro language elements affected by these new data types are listed in the topics for the new data types.

    Binary Index Tag Based on Logical Expressions

    Visual FoxPro includes a new binary, or bitmap, index for creating indexes based on logical expressions, for example, indexes based on deleted records. A binary index can be significantly smaller than a non-binary index and can improve the speed of maintaining indexes. You can create binary indexes using the Table Designer or INDEX command. Visual FoxPro also includes Rushmore optimization enhancements in the SQL engine for deleted records.

    For more information, see Visual FoxPro Index TypesINDEX CommandALTER TABLE – SQL Command, and Indexes Based on Deleted Records.

    Converting Data Types with the CAST( ) Function

    You can convert expressions from one data type to another by using the new CAST( ) function. Using CAST( ) makes it possible for you to create SQL statements more compatible with SQL Server.

    For more information, see CAST( ) Function.

    Get Cursor and Count Records Affected by SQL Pass-Thru Execution

    By using the aCountInfo parameter of the SQLEXEC( ) and SQLMORERESULTS( ) functions, you can get the name of the cursor created and a count of the records affected by the execution of a SQL pass-through statement.

    For more information, see SQLEXEC( ) Function) and SQLMORERESULTS( ) Function.

    Roll-Back Functionality Supported when a SQL Pass-Through Connection Disconnects

    Visual FoxPro now supports the DisconnectRollback property for use with the SQLSETPROP( )SQLGETPROP( )DBSETPROP( ), and DBGETPROP( ) functions. DisconnectRollback is a connection-level property that causes a transaction to be either rolled back or committed when the SQLDISCONNECT( ) function is called for the last connection handle associated with the connection.

    The DisconnectRollback property accepts a logical value.

    • False (.F.) – (Default) The transaction will be committed when the SQLDISCONNECT( ) function is called for the last statement handle associated with the connection.
    • True (.T.) – The transaction is rolled back when the SQLDISCONNECT( ) function is called for the last statement handle associated with the connection.

    The following example shows the DisconnectRollback property set in the DBSETPROP( ) and SQLSETPROP( ) functions.Copy

    DBSETPROP("testConnection","CONNECTION","DisconnectRollback",.T.)
    SQLSETPROP(con,"DisconnectRollback",.T.)
    
    

    For more information, see DisconnectRollback property in SQLSETPROP( ) Function.

    SQLIDLEDISCONNECT( ) Temporarily Disconnects SQL Pass-Through Connections

    You can use the new SQLIDLEDISCONNECT( ) function to allow a SQL Pass-Through connection to be temporarily disconnected. Use the following syntax.Copy

    SQLIDLEDISCONNECT( nStatementHandle )
    
    

    The nStatementHandle parameter is set to the statement handle to be disconnected or 0 if all statement handles should be disconnected.

    The SQLIDLEDISCONNECT( ) function returns the value 1 if it is successful; otherwise, it returns -1.

    The function fails if the specified statement handle is busy or the connection is in manual commit mode. The AERROR( ) function can be used to obtain error information.

    The disconnected connection handle is automatically restored if it is needed for an operation. The original connection data source name is used.

    If a statement handle is temporarily released, the OBDChstmt property returns 0; the OBDChdbc returns 0 if the connection is temporarily disconnected. A shared connection is temporarily disconnected as soon as all of its statement handles are temporarily released.

    For more information, see SQLIDLEDISCONNECT( ) Function.

    Retrieving Active SQL Connection Statement Handles

    You can retrieve information for all active SQL connection statement handles using the new ASQLHANDLES( ) function. ASQLHANDLES( ) creates and uses the specified array to store numeric statement handle references that you can use in other Visual FoxPro SQL functions, such as SQLEXEC( ) and SQLDISCONNECT( )ASQLHANDLES( ) returns the number of active statement handles in use or zero (0) if none are available. For more information, see ASQLHANDLES( ) Function.

    Obtain the ADO Bookmark for the Current Record in an ADO-Based Cursor

    The ADOBookmark property is now supported by the CURSORGETPROP( ) function. Use this property to obtain the ActiveX® Data Objects (ADO) bookmark for the current record in an ADO-based cursor.

    For more information, see ADOBookmark Property in CURSORGETPROP( ) Function.

    If a table is not selected and an alias is not specified, Error 52, “No table is open in the current work area,” is generated. If the cursor selected is not valid, Error 1467, “Property is invalid for local cursors,” is generated.

    Obtain the Number of Fetched Records

    You can obtain the number of fetched records during SQL Pass-Through execution by using the new RecordsFetched cursor property with the CURSORGETPROP( ) function.

    Specifying the RecordsFetched cursor property will return the number of fetched records from an OBDC/ADO-based cursor.

    If records have been deleted or appended locally, the RecordsFetched cursor property may not return the current number of records in the OBDC/ADO-based cursor. In addition, filter conditions are ignored.

    For more information, see RecordsFetched Property in CURSORGETPROP( ) Function.

    Determine if a Fetch is Complete

    You can determine if a fetch process is complete for an OBDC/ADO-based cursor by using the new FetchIsComplete cursor property with the CURSORGETPROP( ) function. Read-only at design time and run time.

    This property is not supported on environment level (work area 0) cursors, tables, and local views.

    The FetchIsComplete cursor property returns a logical expression True (.T.) if the fetch process is complete; otherwise False (.F.) is returned.

    For more information, see FetchIsComplete Property in CURSORGETPROP( ) Function.

    ISMEMOFETCHED( ) Determines Whether a Memo is Fetched

    You can use the ISMEMOFETCHED( ) function to determine whether a Memo field or General field is fetched when you are using delayed memo fetching. For more information about delayed memo fetching, see Speeding Up Data Retrieval.

    The syntax for this function is:

    ISMEMOFETCHED(cFieldName | nFieldNumber [, nWorkArea | cTableAlias ])

    The ISMEMOFETCHED( ) function returns True (.T.) when the Memo field is fetched or if local data is used. ISMEMOFETCHED() returns NULL if the record pointer is positioned at the beginning of the cursor or past the last record.

    For more information, see ISMEMOFETCHED( ) Function.

    Cancel ADO Fetch

    In Visual FoxPro, you can now cancel a lengthy ADO fetch by pressing the ESC key.

    Long Type Name Support

    Visual FoxPro supports using long type names with the following functions, commands, and properties.

    The following table lists the data types along with their long type names and short type names.Expand table

    Data TypeLong Type NameShort Type Name
    CharacterChar, CharacterC
    DateDateD
    DateTimeDatetimeT
    NumericNum, NumericN
    FloatingFloatF
    IntegerInt, IntegerI
    DoubleDoubleB
    CurrencyCurrencyY
    LogicalLogicalL
    MemoMemoM
    GeneralGeneralG
    PicturePictureP
    VarcharVarcharV
    VarbinaryVarbinaryQ
    BlobBlobW

    Visual FoxPro allows ambiguous long type names to be used with the ALTER TABLECREATE CURSORCREATE TABLE, and CREATE FROM commands. If the specified long type name is not a recognized long type name, Visual FoxPro will truncate the specified name to the first character.

    Transaction Support for Free Tables and Cursors

    In prior versions of Visual FoxPro, transactions using the BEGIN TRANSACTION Command were only supported for local and remote data from databases. Transactions involving free tables and cursors are now supported through use of the MAKETRANSACTABLE( ) and ISTRANSACTABLE( ) functions. For more information, see MAKETRANSACTABLE( ) Function and ISTRANSACTABLE( ) Function.

    Specify a Code Page When Using the CREATE TABLE or CREATE CURSOR Commands

    You can specify a code page by including the CODEPAGE clause with the CREATE CURSOR or CREATE TABLE commands.

    When the CODEPAGE clause is specified, the new table or cursor has a code page specified by nCodePage. An error, 1914, “Code page number is invalid”, is generated if an invalid code page is specified.

    The following example creates a table and displays its code page:Copy

    CREATE TABLE Sales CODEPAGE=1251 (OrderID I, CustID I, OrderAmt Y(4))
    
    

    ? CPDBF( )

    For more information, see CREATE CURSOR – SQL CommandCREATE TABLE – SQL Command and Code Pages Supported by Visual FoxPro.

    Convert Character and Memo Data Types Using the ALTER TABLE Command

    Visual FoxPro now supports automatic conversion from character data type to memo data type without loss of data when using the ALTER TABLE command along with the ALTER COLUMN clause. This conversion is also supported when making structural changes using the Table Designer. For more information, see ALTER TABLE – SQL Command.

    BLANK Command Can Initialize Records to Default Value

    You can initialize fields in the current record to their default values as stored in the table’s database container (DBC) by using the DEFAULT [AUTOINC] option when clearing the record with the BLANK command. For more information, see BLANK Command.

    FLUSH Command Writes Data Explicitly to Disk

    Visual FoxPro now includes options and parameters for the FLUSH command and FFLUSH function so you can explicitly save all changes you make to all open tables and indexes. You can also save changes to a specific table by specifying a work area, table alias, or a path and file name. For more information, see FLUSH Command and FFLUSH( ) Function.

    Populate an Array with Aliases Used by a Specified Table

    The new cTableName parameter for the AUSED( ) function makes it possible to filter the created array to contain only the aliases being used for a specified table.

    AUSED(ArrayName [, nDataSessionNumber [, cTableName ]])

    The cTableName parameter accepts the following formats to specify a table, from highest to lowest in priority.

    • DatabaseName!TableName or DatabaseName!ViewName
    • Path\DatabaseName!TableName or Path\DatabaseName!ViewName
    • DBC-defined table name or view in the current DBC in the current data session
    • Simple or full file name

    For more information, see AUSED( ) Function.

    Obtain Last Auto-Increment Value with GETAUTOINCVALUE( )

    You can use the new GETAUTOINCVALUE( ) function to return the last value generated for an autoincremented field within a data session. For more information, see GETAUTOINCVALUE( ) Function.

    SET TABLEPROMPT Controls Prompt to Select Table

    The new SET TABLEPROMPT command controls whether Visual FoxPro prompts the user with the Open Dialog Box (Visual FoxPro) to select a table when one specified cannot be found, such as in SELECT – SQL Command. For more information, see SET TABLEPROMPT Command.

    Use SET VARCHARMAPPING to Control Query Result Set Mappings

    For queries such as SELECT – SQL Command, character data is often manipulated using Visual FoxPro functions and expressions. Since the length of the resulting field value may be important for certain application uses, it is valuable to have this Character data mapped to Varchar data in the result set. The SET VARCHARMAPPING command controls whether Character data is mapped to a Character or Varchar data type. For more information, see SET VARCHARMAPPING Command.

    SET TABLEVALIDATE Expanded

    When a table header is locked during validation, attempts to open the table, for example, with the USE command, generate the message “File is in use (Error 3).” If the table header cannot be locked for a table open operation, you can suppress this message by setting the third bit for the SET TABLEVALIDATE command. You must also set the first bit to validate the record count when the table opens. Therefore, you need to set the SET TABLEVALIDATE command to a value of 5. Also, a fourth bit option (value of 8) is available for Insert operations which checks the table header before the appended record is saved to disk and the table header is modified.

    For more information, see SET TABLEVALIDATE Command.

    SET REFRESH Can Specify Faster Refresh Rates

    You can specify fractions of a second for the nSeconds2 parameter to a minimum of 0.001 seconds. You can also specify the following values for the optional second parameter:

    • -1 – Always read data from a disk.
    • 0 – Always use data in memory buffer but do not refresh buffer.

    The Table refresh interval check box on the Data tab of the Options dialog box now also accepts fractional values.

    For more information, see SET REFRESH Command and Data Tab, Options Dialog Box.

    SET REFRESH Can Differentiate Values for Each Cursor

    You can use the new Refresh property with the CURSORGETPROP( ) function to differentiate the SET REFRESH values for individual cursors. The default setting is -2, which is a global value. This value is not available with the SET REFRESH command.

    The Refresh property is available at the Data Session and Cursor level. The default setting for a Data Session level is -2 and the default value for a Cursor level is the current session’s level setting. If the global level setting is set to 0, the Cursor level setting is ignored.

    If a table is not currently selected and an alias is not specified, Error 52, “No table is open in the current work area,” is generated.

    For more information, see Refresh Property in CURSORGETPROP( ) Function.

    SET( ) Determines SET REPROCESS Command Settings

    You can now use the following syntax with the SET( ) function to determine how the SET REPROCESS command was declared.Expand table

    SET CommandValue Returned
    REPROCESS, 2Current session setting type (0 – attempts, 1 – seconds)
    REPROCESS, 3System session setting type (0 – attempts, 1 – seconds)

    For more information, see SET( ) Function and SET REPROCESS Command.

    Log Output from SYS(3054) Using SYS(3092)

    You can use the new SYS(3092) function in conjunction with SYS(3054) to record the resulting output to a file.

    SYS( 3092 [, cFileName [, lAdditive ]])

    The cFileName parameter specifies the file to echo the SYS(3054) output to. Sending an empty string to cFileName will deactivate output recording to the file.

    The default value for lAdditive is False (.F.). This specifies that new output will overwrite the previous contents of the specified file. To append new output to the specified file, set lAdditive to True (.T.).

    SYS(3092) returns the name of the current echo file if it is active; otherwise, it returns an empty string.

    SYS(3054) and SYS(3092) are global settings — in a multithreaded runtime they are scoped to a thread. Each function can be changed independently from each other.

    These functions are not available in the Visual FoxPro OLE DB Provider.

    For more information, see SYS(3054) – Rushmore Query Optimization Level and SYS(3092) – Output Rushmore Query Optimization Level.

    Purge Cached Memory for Specific Work Area Using SYS(1104)

    You can optionally specify the alias or work area of a specified table or cursor for which cached memory is purged. For more information, see SYS(1104) – Purge Memory Cache.

    New Table Types for SYS(2029)

    The SYS(2029) function returns new values for tables that contain AutoincVarcharVarbinary or Blob fields. For more information, see SYS(2029) – Table Type.

    Map Remote Unicode Data to ANSI Using SYS(987)

    Use SYS(987) to map remote Unicode data retrieved through SQL pass-through or remote views to ANSI. This function can be used to retrieve remote Varchar data as ANSI for use with Memo fields. This setting is a global setting across all data sessions so should be used with care. For more information, see SYS(987) – Map Remote Data to ANSI.

    Memo and Field tips in a BROWSE or Grid

    When the mouse pointer is positioned over a Memo field cell in a Browse window or Grid control, a Memo Tip window displays the contents of the Memo field.

    For other field types, positioning the mouse pointer over the field displays the field contents in a Field Tip window when the field is sized smaller than its contents.

    Memo Tip windows display no more than 4 kilobytes of text, and are not displayed for binary data. A Memo Tip window is displayed until the mouse pointer is moved from the Memo field. The _TOOLTIPTIMEOUT System Variable determines how long a Field Tip window is displayed.

    You can disable Memo Tips by setting the _SCREEN ShowTips Property to False (.F.).

    Memo and Field Tips will also be displayed for Grid controls if both _SCREEN and the form’s ShowTips property are set to True (.T.). Additionally, the ToolTipText Property for the field’s grid column Textbox control must contain an empty string.

    Specify Code Pages

    You can specify the code page used to decode data when XML is being parsed and to encode data when UTF-8 encoded XML is generated. The following language changes are available:

    • nCodePage ParameterTo specify code pages, you can use the nCodePage parameter for the following XMLToTable methods:CopyXMLTable.ToCursor ( [ lAppend [, cAlias [, nCodePage ]]] ) XMLTable.ChangesToCursor( [ cAlias [, lIncludeUnchangedData [, nCodePage ]]] ) XMLTable.ApplyDiffgram( [ cAlias [, oCursorAdapter [, lPreserveChanges [, nCodePage ]]]] )
    • CodePage and UseCodePage PropertiesUse the CodePage Property and UseCodePage Property to specify code pages when you use the following classes:CopyXMLAdapter.CodePage = nValue XMLTable.CodePage = nValue XMLField.CodePage = nValue
    • Flag 32768The flag 32768 is available for the following functions and class:CopyCursorAdaptor.Flags = nCodePage XMLTOCURSOR( eExpression | cXMLFile [, cCursorName [, nFlags ]]) CURSORTOXML(nWorkArea | cTableAlias, cOutput [, nOutputFormat [, nFlags [, nRecords [, cSchemaName [, cSchemaLocation [, cNameSpace ]]]]]]) XMLUPDATEGRAM( [ cAliasList [, nFlags [, cSchemaLocation]]]) The nCodePage parameter must match a recognized Visual FoxPro code page.

    For more information, see Code Pages Supported by Visual FoxPro.

    MapVarchar Property Maps to Varchar, Varbinary, and Blob Data Types

    For CursorAdapter and XMLAdapter classes, you can use the MapVarchar property to map to Varchar data types. To map to Varbinary and Blob data types, you can use the MapBinary property.

    The XMLTOCURSOR( ) Function contains several new flags to support mapping of Char and base64Binary XML field types to new Fox data types.

    For more information, see the MapVarchar Property and MapBinary Property.

    Handling Conflict Checks with Properties for CursorAdapter Class

    You can better handle conflicts when performing update and delete operations using the commands specified by the UpdateCmd and DeleteCmd properties for CursorAdapter objects by using the new ConflictCheckType and ConflictCheckCmd properties for CursorAdapter objects.

    You can use ConflictCheckType to specify how to handle a conflict check during an update or delete operation. When ConflictCheckType is set to 4, you can use ConflictCheckCmd to specify a custom command to append to the end of the commands in the UpdateCmd and DeleteCmd properties.

     Note

    Visual FoxPro 8.0 Service Pack 1 includes the ConflictCheckType and ConflictCheckCmd properties.

    For more information, see ConflictCheckType Property and ConflictCheckCmd Property.

    Improved DataEnvironment Handling with UseCursorSchema and NoData Properties

    You can specify default settings for CursorFill Method calls made without the first two parameters by setting these properties. For more information, see UseCursorSchema Property and NoData Property.

    Timestamp Field Support

    The new TimestampFieldList property lets you specify a list of timestamp fields for the cursor created by the CursorAdapter. For more information see TimestampFieldList Property.

    Auto-Refresh Support

    There are a number of scenarios where you might want to have cursor data refreshed from a remote data source after an Insert/Update operation has occurred. These include following scenarios:

    • A table has an auto-increment field that also acts as a primary key.
    • A table has a timestamp field, and that field must be refreshed from the database after each Insert/Update in order to allow successful subsequent updates to the record when WhereType=4 (key and timestamp).
    • A table contains some fields which have DEFAULT values or triggers defined that will cause changes to occur.

    The following new properties have been added to the CursorAdapter class for Auto-Refresh support:Expand table

    PropertyDescription
    InsertCmdRefreshFieldListList of fields to refresh after Insert command executes.
    InsertCmdRefreshCmdSpecifies the command to refresh the record after Insert command executes.
    InsertCmdRefreshKeyFieldListList of key fields to refresh in record after Insert command executes.
    UpdateCmdRefreshFieldListList of fields to refresh after Update command executes.
    UpdateCmdRefreshCmdSpecifies the command to refresh the record after Update command executes.
    UpdateCmdRefreshKeyFieldListList of key fields to refresh the record after Update command executes.
    RefreshTimestampEnables automatic refresh for fields in TimestampFieldList during Insert/Update.

    For more information about how Visual FoxPro updates remote data using a CursorAdapter, see Data Access Management Using CursorAdapters. Also, see InsertCmdRefreshCmd PropertyInsertCmdRefreshFieldList PropertyInsertCmdRefreshKeyFieldList PropertyUpdateCmdRefreshCmd PropertyUpdateCmdRefreshFieldList PropertyUpdateCmdRefreshKeyFieldList Property and RefreshTimeStamp Property.

    On Demand Record Refresh

    In Visual FoxPro 8.0, the REFRESH( ) Function provides on demand record refresh functionality for local and remote views, however, it does not support this for the CursorAdapter. Visual FoxPro 9.0 extends REFRESH( ) support to the CursorAdapter and provides some additional capabilities:Expand table

    MemberDescription
    RecordRefresh methodRefreshes the current field values for the target records. Use the CURVAL( ) Function to determine current field values.
    BeforeRecordRefresh eventOccurs immediately before the RecordRefresh method is executed.
    AfterRecordRefresh eventOccurs after the RecordRefresh method is executed.
    RefreshCmdDataSourceType propertySpecifies the data source type to be used for the RecordRefresh method.
    RefreshCmdDataSource propertySpecifies the data source to be used for the RecordRefresh method.
    RefreshIgnoreFieldList propertyList of fields to ignore during RecordRefresh operation
    RefreshCmd propertySpecifies the command to refresh rows when RecordRefresh is executed.
    RefreshAlias propertySpecifies the alias of read-only cursor used as a target for the refresh operation.

    For more information, see RecordRefresh MethodBeforeRecordRefresh EventAfterRecordRefresh EventRefreshCmdDataSourceType PropertyRefreshCmdDataSource PropertyRefreshIgnoreFieldList PropertyRefreshCmd Property and RefreshAlias Property.

    Delayed Memo Fetch

    The CursorAdapter class has a FetchMemo Property, which when set to False (.F.) in Visual FoxPro 9.0 places the cursor in Delayed Memo Fetch mode similar to Remote Views. Delayed Memo Fetch Mode prevents the contents of Memo fields from being fetched using CursorFill Method or CursorRefresh Method. An attempt to fetch content for a Memo field is done when the application attempts to access the value. The following CursorAdapter enhancements provide support for Delayed Memo Fetch:Expand table

    MemberDescription
    DelayedMemoFetch methodPerforms a delayed Memo field fetch for a target record in a cursor in a CursorAdapter object.
    FetchMemoDataSourceType propertySpecifies the data source type used for the DelayedMemoFetch method.
    FetchMemoDataSource propertySpecifies the data source used for the DelayedMemoFetch method.
    FetchMemoCmdList propertySpecifies a list of Memo field names and their associated fetch commands.

    For more information, see DelayedMemoFetch MethodFetchMemoDataSourceType PropertyFetchMemoDataSource Property and FetchMemoCmdList Property.

    UseTransactions Property

    The new UseTransactions property specifies whether the CursorAdapter should use transactions when sending Insert, Update or Delete commands through ADO or ODBC. For more information, see UseTransactions Property.

    DEFAULT and CHECK Constraints Respected

    In Visual FoxPro 9.0, DEFAULT values and table and field level CHECK constraints are supported for XML, Native, ADO and ODBC data sources. In Visual FoxPro 8.0, DEFAULT values and table and field level CHECK constraints are only supported for an XML data source. For the DEFAULT values and CHECK constraints to be applied to a cursor, call the CursorFill Method with the lUseSchema parameter set to True (.T.). For more information, see CursorSchema Property.

    Remote Data Type Conversion for Logical Data

    When you move data between a remote server and Visual FoxPro, Visual FoxPro uses ODBC or ADO data types to map remote data types to local Visual FoxPro data types. In Visual FoxPro 9.0, certain ODBC and ADO data types can now be mapped to a logical data type in remote views and the CursorAdapter object. For more information, see Data Type Conversion Control.

    ADOCodePage Property

    When working with an ADO data source for your CursorAdapter, you may want to specify a code page to use for character data translation. The new ADOCodePage property allows you to specify this code page. For more information, see ADOCodePage Property.

    Read and Write Nested XML Documents

    You can read to and write from your relational database into XML documents using nesting to handle the relationships between tables. You accomplish this using the RespectNesting Property of the XMLAdapter class. The XMLTable class has the Nest MethodUnnest Method and the following properties to handle nesting.

    For more information, see the XMLAdapter Class and the XMLTable Class.

    LoadXML Method Can Accept Any XML Document

    The LoadXML method accepts any XML document with a valid schema. Previously, the method required that the schema follow the format of a Visual Studio generated dataset. When you use the LoadXML method to read an XML document with a schema different from a Visual Studio generated dataset, the properties for the XMLAdapterXMLName, and XMLPrefix properties are set to empty (“”). The XMLAdapterXMLNamespace property becomes equal to the target Namespace attribute value for the schema node and each XML element becomes a complexType and is mapped to an XMLTable object. The XMLNamespace property is set to namespaceURI for the element.

    If you set the XMLAdapterRespectNesting property to True (.T.), the top level element declaration is ignored if it is referenced from some other complex element. For that case, the XMLTable object for the referenced element is nested into the XMLTable for the element that references it.

    For more information, see LoadXML Method.

    XPath Expressions Can Access Complex XML Documents

    You can use XPath expressions to access complex XML documents and the new properties for reading the nodes within the document. For example, you might want to filter record nodes, restore relationships based on foreign key fields, use an element’s text as data for a field, or access XML that uses multiple XML namespaces. The following properties provide you with the ability to read the XML at the XMLAdapter level, XMLTable level, or the XMLField level.

    You can use the following table to determine the node within the XML document that you want to start reading.

    For example, if you use an XPath expression in the XMLName property for an XMLAdapter, reading begins at the first nodeExpand table

    To readClassContext node
    From the first found XML node:XMLAdapterIXMLDOMElement property
    All found XML nodes and use each node as a single record:XMLTableXMLAdapter object
    The first found XML node and use its text as a field value:XMLFieldXMLTable object

    The following methods do not support the use of XPath expressions in the XMLName property:

    • The ApplyDiffgram and ChangesToCursor methods do not support XPath expressions for XMLAdapter and XMLTable objects.
    • The ToCursor method does not support an XPath expression for XMLAdapter when the IsDiffgram property is set to True (.T.).
    • The ToXML method does not support XPath expressions for XMLAdapter and XMLTable objects and ignores XMLField objects that use XPath expressions.

    For more information about XPath expressions, see the XPath Reference in the Microsoft Core XML Services (MSXML) 4.0 SDK in the MSDN library at https://msdn.microsoft.com/library.

    Cursor to XML Functions

    Support for the following functions has been added to the OLE DB Provider for Visual FoxPro:

    When used in the OLE DB Provider for Visual FoxPro, the _VFP VFPXMLProg property is not supported for the CURSORTOXML( )XMLTOCURSOR( ) and XMLUPDATEGRAM( ) functions because the _VFP system variable is not supported in the OLE DB Provider.

    EXECSCRIPT Supported in the Visual FoxPro OLE DB Provider

    You can use the EXECSCRIPT( ) function with the Visual FoxPro OLE DB Provider. For more information, see EXECSCRIPT( ) Function.

    Returning a Rowset from a Cursor in the Visual FoxPro OLE DB Provider

    You can use the new SETRESULTSET( )GETRESULTSET( ), and CLEARRESULTSET( ) functions to mark a cursor or table that has been opened by the Visual FoxPro OLE DB Provider, retrieve the work area of the marked cursor, and clear the marker flag from a marked cursor. By marking a cursor or table, you can retrieve a rowset that is created from the marked cursor or table from a database container (DBC) stored procedure when the OLE DB Provider completes command execution.

    For more information, see SETRESULTSET( ) FunctionGETRESULTSET( ) Function, and CLEARRESULTSET( ) Function.

    See Also

    Reference

    Guide to Reporting Improvements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Other Resources

    What’s New in Visual FoxPro

    SQL Language Improvements

    • Article
    • 07/09/2007

    In this article

    1. Expanded Capacities
    2. Subquery Enhancements
    3. Sub-SELECT in the FROM Clause
    4. ORDER BY with Field Names in the UNION clause

    Show 5 more

    The SELECT – SQL Command and other SQL commands have been substantially enhanced in Visual FoxPro 9.0. This topic describes the enhancements made to these commands, and new commands that affect SQL performance.

    Expanded Capacities

    Several SELECT – SQL command limitations have been removed or increased in Visual FoxPro 9.0. The following table lists the areas where limitations have been removed or increased.Expand table

    CapacityDescription
    Number of Joins and Subqueries in a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on the total number of join clauses and subqueries in a SELECT – SQL command. The previous limit was nine.
    Number of UNION clauses in a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on number of UNION clauses in a SQL SELECT statement. The previous limit was nine.
    Number of tables referenced a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on the number of tables and aliases referenced in a SQL SELECT statement. The previous limit was 30.
    Number of arguments in an IN( ) clauseVisual FoxPro 9.0 removes the limit of 24 values in the IN (Value_Set) clause for the WHERE clause. However, the number of values remains subject to the setting of SYS(3055) – FOR and WHERE Clause Complexity. For functionality changes concerning the IN clause, see Changes in Functionality for the Current Release.

    Subquery Enhancements

    Visual FoxPro 9.0 provides more flexibility in subqueries. For example, multiple subqueries are now supported. The following describes the enhancements to subqueries in Visual FoxPro 9.0.

    Multiple Subqueries

    Visual FoxPro 9.0 supports multiple subquery nesting, with correlation allowed to the immediate parent. There is no limit to the nesting depth. In Visual FoxPro 8.0, error 1842 (SQL: Subquery nesting is too deep) was generated when more than one level of subquery nesting occurred.

    The following is the general syntax for multiple subqueries.

    SELECT … WHERE … (SELECT … WHERE … (SELECT …) …) …

    Examples

    The following example queries, which will generate an error in Visual FoxPro 8.0, are now supported in Visual FoxPro 9.0.Copy

    CREATE CURSOR MyCursor (field1 I)
    INSERT INTO MyCursor VALUES (0)
    
    CREATE CURSOR MyCursor1 (field1 I)
    INSERT INTO MyCursor1 VALUES (1)
    
    CREATE CURSOR MyCursor2 (field1 I)
    INSERT INTO MyCursor2 VALUES (2)
    
    SELECT * FROM MyCursor T1 WHERE EXISTS ;
        (SELECT * from MyCursor1 T2 WHERE NOT EXISTS ;
        (SELECT * FROM MyCursor2 T3))
    
    *** Another multiple subquery nesting example ***
    SELECT * FROM table1 WHERE table1.iid IN ;
        (SELECT table2.itable1id FROM table2 WHERE table2.iID IN ;
        (SELECT table3.itable2id FROM table3 WHERE table3.cValue = "value"))
    
    

    GROUP BY in a Correlated Subquery

    Many queries can be evaluated by executing a subquery once and substituting the resulting value or values into the WHERE clause of the outer query. In queries that include a correlated subquery (also known as a repeating subquery), the subquery depends on the outer query for its values. This means that the subquery is executed repeatedly, once for each row that might be selected by the outer query.

    Visual FoxPro 8.0 does not allow using GROUP BY in correlated subquery, and generates error 1828 (SQL: Illegal GROUP BY in subquery). Visual FoxPro 9.0 removes this limitation and supports GROUP BY for correlated subqueries allowed to return more than one record.

    The following is the general syntax for the GROUP BY clause in a correlated subquery.

    SELECT … WHERE … (SELECT … WHERE … GROUP BY …) …

    Examples

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.Copy

    CLOSE DATABASES ALL
    CREATE CURSOR MyCursor1 (field1 I, field2 I, field3 I)
    INSERT INTO MyCursor1 VALUES(1,2,3)
    CREATE CURSOR MyCursor2 (field1 I, field2 I, field3 I)
    INSERT INTO MyCursor2 VALUES(1,2,3)
    
    SELECT * from MyCursor1 T1 WHERE field1;
       IN (SELECT MAX(field1) FROM MyCursor2 T2 ;
       WHERE T2.field2=T1.FIELD2 GROUP BY field3)
    
    

    TOP N in a Non-Correlated Subquery

    Visual FoxPro 9.0 supports the TOP N clause in a non-correlated subquery. The ORDER BY clause should be present if the TOP N clause is used, and this is the only case where it is allowed in subquery.

    The following is the general syntax for the TOP N clause in a non-correlated subquery.

    SELECT … WHERE … (SELECT TOP nExpr [PERCENT] … FROM … ORDER BY …) …

    Examples

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.Copy

    CLOSE DATABASES ALL
    CREATE CURSOR MyCursor1 (field1 I, field2 I, field3 I)
    INSERT INTO MyCursor1 VALUES(1,2,3)
    CREATE CURSOR MyCursor2 (field1 I, field2 I, field3 I)
    INSERT INTO MyCursor2 VALUES(1,2,3)
    
    SELECT * FROM MyCursor1 WHERE field1 ;
       IN (SELECT TOP 5 field2 FROM MyCursor2 order by field2)
    
    

    Subqueries in a SELECT List

    Visual FoxPro 9.0 allows a subquery as a column or a part of expression in a projection. A subquery in a projection has exactly the same requirements as a subquery used in a comparison operation. If a subquery does not return any records, NULL value is returned.

    In Visual FoxPro 8.0, an attempt to use a subquery as a column or a part of expression in a projection would generate error 1810 (SQL: Invalid use of subquery).

    The following is the general syntax for a subquery in a SELECT list.

    SELECT … (SELECT …) … FROM …

    Example

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.Copy

    SELECT T1.field1, (SELECT field2 FROM MyCursor2 T2;
       WHERE T2.field1=T1.field1) FROM MyCursor1 T1
    
    

    Aggregate functions in a SELECT List of a Subquery

    In Visual FoxPro 9.0, aggregate functions are now supported in a SELECT list of a subquery compared using the comparison operators <, <=, >, >= followed by ALL, ANY, or SOME. See Considerations for SQL SELECT Statements for more information about aggregate functions.

    Example

    The following example demonstrates the use of an aggregate function (the COUNT( ) function) in a SELECT list of a subquery.Copy

    CLOSE DATABASES ALL 
    
    CREATE CURSOR MyCursor (FIELD1 i)
    INSERT INTO MyCursor VALUES (6)
    INSERT INTO MyCursor VALUES (0)
    INSERT INTO MyCursor VALUES (1)
    INSERT INTO MyCursor VALUES (2)
    INSERT INTO MyCursor VALUES (3)
    INSERT INTO MyCursor VALUES (4)
    INSERT INTO MyCursor VALUES (5)
    INSERT INTO MyCursor VALUES (-1)
    
    CREATE CURSOR MyCursor2 (FIELD2 i)
    INSERT INTO MyCursor2  VALUES (1)
    INSERT INTO MyCursor2  VALUES (2)
    INSERT INTO MyCursor2  VALUES (2)
    INSERT INTO MyCursor2  VALUES (3)
    INSERT INTO MyCursor2  VALUES (3)
    INSERT INTO MyCursor2  VALUES (3)
    INSERT INTO MyCursor2  VALUES (4)
    INSERT INTO MyCursor2  VALUES (4)
    INSERT INTO MyCursor2  VALUES (4)
    INSERT INTO MyCursor2  VALUES (4)
    
    SELECT * FROM MYCURSOR WHERE field1;
       < ALL (SELECT count(*) FROM MyCursor2 GROUP BY field2) ;
       INTO CURSOR MyCursor3
    BROWSE
    
    

    Correlated Subqueries Allow Complex Expressions to be Compared with Correlated Field

    In Visual FoxPro 8.0, correlated fields can only be referenced in the following forms:

    correlated field <comparison> local field

    -or-

    local field <comparison> correlated field

    In Visual FoxPro 9.0. correlated fields support comparison to local expressions, as shown in the following forms:

    correlated field <comparison> local expression

    -or-

    local expression <comparison> correlated field

    A local expression must use at least one local field and cannot reference any outer (correlated) field.

    Example

    In the following example, a local expression (MyCursor2.field2 / 2) is compared to a correlated field (MyCursor.field1).Copy

    SELECT * FROM MyCursor ;
       WHERE EXISTS(SELECT * FROM MyCursor2  ;
       WHERE MyCursor2.field2 / 2 > MyCursor.field1)
    
    

    Changes for Expressions Compared with Subqueries.

    In Visual FoxPro 8.0, the left part of a comparison using the comparison operators [NOT] IN, <, <=, =, ==, <>, !=, >=, >, ALL, ANY, or SOME with a subquery must reference one and only one table from the FROM clause. In case of a comparison with correlated subquery, the table must also be the correlated table.

    In Visual FoxPro 9.0, comparisons work in the following ways:

    • The expression on the left side of an IN comparison must reference at least one table from the FROM clause.
    • The left part for the conditions =, ==, <>, != followed by ALL, SOME, or ANY must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (SELECT TOP…) must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (SELECT <aggregate function>…) must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (subquery with GROUP BY and/or HAVING) must reference at least one table from the FROM clause.

    In Visual FoxPro 9.0, the left part of a comparison that does not come from the list (for example, ALL, SOME, or ANY are not included) doesn’t have to reference any table from the FROM clause.

    In all cases, the left part of the comparison is allowed to reference more than one table from the FROM clause. For a correlated subquery, the left part of the comparison does not have to reference the correlated table.

    Subquery in an UPDATE – SQL Command SET List

    In Visual FoxPro 9.0, the UPDATE – SQL Command now supports a subquery in the SET clause.

    A subquery in a SET clause has exactly the same requirements as a subquery used in a comparison operation. If the subquery does not return any records, the NULL value is returned.

    Only one subquery is allowed in a SET clause. If there is a subquery in the SET clause, subqueries in the WHERE clause are not allowed.

    The following is the general syntax for a subquery in the SET clause.

    UPDATE … SET … (SELECT …) …

    Example

    The following example demonstrates the use of a subquery in the SET clause.Copy

    CLOSE DATA
    CREATE CURSOR MyCursor1 (field1 I , field2 I NULL)
    
    INSERT INTO MyCursor1 VALUES (1,1)
    INSERT INTO MyCursor1 VALUES (2,2)
    INSERT INTO MyCursor1 VALUES (5,5)
    INSERT INTO MyCursor1 VALUES (6,6)
    INSERT INTO MyCursor1 VALUES (7,7)
    INSERT INTO MyCursor1 VALUES (8,8)
    INSERT INTO MyCursor1 VALUES (9,9)
    
    CREATE CURSOR MyCursor2 (field1 I , field2 I)
    
    INSERT INTO MyCursor2 VALUES (1,10)
    INSERT INTO MyCursor2 VALUES (2,20)
    INSERT INTO MyCursor2 VALUES (3,30)
    INSERT INTO MyCursor2 VALUES (4,40)
    INSERT INTO MyCursor2 VALUES (5,50)
    INSERT INTO MyCursor2 VALUES (6,60)
    INSERT INTO MyCursor2 VALUES (7,70)
    INSERT INTO MyCursor2 VALUES (8,80)
    
    UPDATE MyCursor1 SET field2=100+(SELECT field2 FROM MyCursor2 ;
      WHERE MyCursor2.field1=MyCursor1.field1) WHERE field1>5
    
    SELECT MyCursor1
    LIST
    
    

    Sub-SELECT in the FROM Clause

    A sub-SELECT is often referred to as a derived table. Derived tables are SELECT statements in the FROM clause referred to by an alias or a user-specified name. The result set of the SELECT in the FROM clause creates a table used by the outer SELECT statement. Visual FoxPro 9.0 permits the use of a subquery in the FROM clause.

    A sub-SELECT should be enclosed in parentheses and an alias is required. Correlation is not supported. A sub-SELECT has the same syntax limitations as the SELECT command, but not the subquery syntax limitations. All sub-SELECTs are executed before the top most SELECT is evaluated.

    The following is the general syntax for a subquery in the FROM clause.

    SELECT … FROM (SELECT …) [AS] Alias…

    Example

    The following example demonstrates the use of a subquery in the FROM clause.Copy

    SELECT * FROM (SELECT * FROM MyCursor T1;
       WHERE field1 = (SELECT T2.field2 FROM MyCursor1 T2;
       WHERE T2.field1=T1.field2);
       UNION SELECT * FROM MyCursor2;
       ORDER BY 2 desc) AS subquery
    
    

    ** Note that the following code will generate an error ** SELECT * FROM (SELECT TOP 5 field1 FROM MyCursor) ORDER BY field1

    ORDER BY with Field Names in the UNION clause

    When using a UNION clause in Visual FoxPro 8.0, you are forced to use numeric references in the ORDER BY clause. In Visual FoxPro 9.0, this restriction has been removed and you can use field names.

    The referenced fields must be present in the SELECT list (projection) for the last SELECT in the UNION; that projection is used for ORDER BY operation.

    Example

    The following example demonstrates the use of a field names in the ORDER BY clause.Copy

    CLOSE DATABASES all
    CREATE CURSOR MyCursor(field1 I,field2 I)
    INSERT INTO MyCursor values(1,6)
    INSERT INTO MyCursor values(2,5)
    INSERT INTO MyCursor values(3,4)
    
    SELECT field1, field2, .T. AS FLAG,1 FROM MyCursor;
       WHERE field1=1;
       UNION ;
       SELECT field1, field2, .T. AS FLAG,1 FROM MyCursor;
       WHERE field1=3;
       ORDER BY field2 ;
       INTO CURSOR TEMP READWRITE
    
    BROWSE NOWAIT
    
    

    Optimized TOP N Performance

    In Visual FoxPro 8.0 and earlier versions, when using the TOP N [PERCENT] clause all records are sorted and then the TOP N are extracted. In Visual FoxPro 9.0, performance has been improved by eliminating records that do not qualify for the TOP N from the sort process as early as possible.

    The TOP N optimization is done only if the SET ENGINEBEHAVIOR Command is set to 90.

    Optimization requires that TOP N return no more than N records (this is not the case for Visual FoxPro 8.0 and earlier versions) which is enforced if SET ENGINEBEHAVIOR is set to 90.

    TOP N PERCENT cannot be optimized unless the whole result set can be read into memory at once.

    Improved Optimization for Multiple Table OR Conditions

    Visual FoxPro 9.0 provides for improved Rushmore optimization involving multi-table OR conditions. Visual FoxPro uses multi-table OR conditions to Rushmore optimize filter conditions for a table as long as both sides of the condition can be optimized. The following example shows this:Copy

    CLEAR
    CREATE CURSOR Test1 (f1 I)
    FOR i=1 TO 20
      INSERT INTO Test1 VALUES (I)
    NEXT 
    INDEX ON f1 TAG f1
    CREATE CURSOR Test2 (f2 I)
    FOR i=1 TO 20
      INSERT INTO Test2 VALUES (I)
    NEXT
    INDEX ON f2 TAG f2
    SYS(3054,12)
    SELECT * from Test1, Test2 WHERE (f1 IN (1,2,3) AND f2 IN (17,18,19)) OR ;
      (f2 IN (1,2,3) AND f1 IN (17,18,19)) INTO CURSOR Result
    SYS(3054,0)
    
    

    In this scenario, table Test1 can be Rushmore optimized using the following condition:

    (f1 IN (1,2,3)  OR f1 IN (17,18,19))and table Test2 with the following:

    (f2 IN (17,18,19) OR f2 IN (1,2,3))

    Support for Local Buffered Data

    At times it can be beneficial to use SELECT – SQL to select records from a local buffered cursor in which the table has not been updated. Many times when creating controls like grids, list boxes, and combo boxes it is necessary to consider newly added records which have not yet been committed to disk. Currently, SQL statements are based on content that is already committed to disk.

    Visual FoxPro 9.0 provides language enhancements that allow you to specify if the data returned by a SELECT – SQL command is based on buffered data or data written directly to disk.

    The SELECT – SQL command now supports a WITH … BUFFERING clause that lets you specify if retrieved data is based on buffered data or data written directly to disk. For more information, see SELECT – SQL Command – WITH Clause.

    If you do not include the BUFFERING clause, the retrieved data is then determined by the setting for SET SQLBUFFERING command. For more information, see the SET SQLBUFFERING Command.

    Enhancements to other SQL Commands

    The following sections describe enhancements made to the INSERT – SQL CommandUPDATE – SQL Command, and DELETE – SQL Command commands in Visual FoxPro 9.0.

    UNION Clause in the INSERT – SQL Command

    In Visual FoxPro 9.0, a UNION clause is now supported in the INSERT – SQL Command.

    The following is the general syntax for the UNION clause.

    INSERT INTO … SELECT … FROM … [UNION SELECT … [UNION …]]

    Example

    The following example demonstrates the use of a UNION clause in INSERT-SQL.Copy

    CLOSE DATABASES ALL
    CREATE CURSOR MyCursor (field1 I,field2 I)
    CREATE CURSOR MyCursor1 (field1 I,field2 I)
    CREATE CURSOR MyCursor2 (field1 I,field2 I)
    
    INSERT INTO MyCursor1 VALUES (1,1)
    INSERT INTO MyCursor2 VALUES (2,2)
    
    INSERT INTO MyCursor SELECT * FROM MyCursor1 UNION SELECT * FROM MyCursor2
    
    SELECT MyCursor
    LIST
    
    

    Correlated UPDATE – SQL Commands

    Visual FoxPro 9.0 now supports correlated updates with the UPDATE – SQL Command.

    If a FROM clause is included in the UPDATE -SQL command, then the name after UPDATE keyword defines the target for the update operation. This name can be a table name, an alias, or a file name. The following logic is used to select the target table:

    • If the name matches an implicit or explicit alias for a table in the FROM clause, then the table is used as a target for the update operation.
    • If the name matches the alias for the cursor in the current data session, then the cursor is used as a target.
    • A table or file with the same name is used as a target.

    The UPDATE -SQL command FROM clause has the same syntax as the FROM clause in the SELECT – SQL command with the following limitations:

    • The target table or cursor cannot be involved in an OUTER JOIN as a secondary table.
    • The target cursor cannot be a subquery result.
    • All other JOINs can be evaluated before joining the target table.

    The following is the general syntax for a correlated UPDATE command.

    UPDATE … SET … FROM … WHERE …

    Example

    The following example demonstrates a correlated update using the UPDATE -SQL command.Copy

    CLOSE DATABASES ALL
    
    CREATE CURSOR MyCursor1 (field1 I , field2 I NULL,field3 I NULL)
    INSERT INTO MyCursor1 VALUES (1,1,0)
    INSERT INTO MyCursor1 VALUES (2,2,0)
    INSERT INTO MyCursor1 VALUES (5,5,0)
    INSERT INTO MyCursor1 VALUES (6,6,0)
    INSERT INTO MyCursor1 VALUES (7,7,0)
    INSERT INTO MyCursor1 VALUES (8,8,0)
    INSERT INTO MyCursor1 VALUES (9,9,0)
    
    CREATE CURSOR MyCursor2 (field1 I , field2 I)
    INSERT INTO MyCursor2 VALUES (1,10)
    INSERT INTO MyCursor2 VALUES (2,20)
    INSERT INTO MyCursor2 VALUES (3,30)
    INSERT INTO MyCursor2 VALUES (4,40)
    INSERT INTO MyCursor2 VALUES (5,50)
    INSERT INTO MyCursor2 VALUES (6,60)
    INSERT INTO MyCursor2 VALUES (7,70)
    INSERT INTO MyCursor2 VALUES (8,80)
    
    CREATE CURSOR MyCursor3 (field1 I , field2 I)
    INSERT INTO MyCursor3 VALUES (6,600)
    INSERT INTO MyCursor3 VALUES (7,700)
    
    UPDATE MyCursor1 SET MyCursor1.field2=MyCursor2.field2, field3=MyCursor2.field2*10 FROM MyCursor2 ;
      WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1
    
    SELECT MyCursor1
    LIST
    
    UPDATE MyCursor1 SET MyCursor1.field2=MyCursor3.field2 FROM MyCursor2, MyCursor3  ;
      WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 AND MyCursor2.field1=MyCursor3.field1
    
    SELECT MyCursor1
    LIST
    
    

    Correlated DELETE – SQL Commands

    Visual FoxPro 9.0 now supports correlated deletions with the DELETE – SQL Command.

    If a FROM clause has more than one table, the name after the DELETE keyword is required and it defines the target for the delete operation. This name can be a table name, an alias or a file name. The following logic is used to select the target table:

    • If the name matches an implicit or explicit alias for a table in the FROM clause, then the table is used as a target for the update operation.
    • If the name matches the alias for the cursor in the current data session, then the cursor is used as a target.
    • A table or file with the same name is used as a target.

    The DELETE -SQL command FROM clause has the same syntax as the FROM clause in the SELECT – SQL command with the following limitations:

    • The target table or cursor cannot be involved in an OUTER JOIN as a secondary table.
    • The target cursor cannot be a subquery result.
    • It should be possible to evaluate all other JOINs before joining the target table.

    The following is the general syntax for a correlated DELETE command.

    DELETE [alias] FROM alias1 [, alias2 … ] … WHERE …

    Example

    The following example demonstrates a correlated deletion using the DELETE -SQL command.Copy

    CLOSE DATABASES ALL 
    
    CREATE CURSOR MyCursor1 (field1 I , field2 I NULL,field3 I NULL)
    INSERT INTO MyCursor1 VALUES (1,1,0)
    INSERT INTO MyCursor1 VALUES (2,2,0)
    INSERT INTO MyCursor1 VALUES (5,5,0)
    INSERT INTO MyCursor1 VALUES (6,6,0)
    INSERT INTO MyCursor1 VALUES (7,7,0)
    INSERT INTO MyCursor1 VALUES (8,8,0)
    INSERT INTO MyCursor1 VALUES (9,9,0)
    
    CREATE CURSOR MyCursor2 (field1 I , field2 I)
    INSERT INTO MyCursor2 VALUES (1,10)
    INSERT INTO MyCursor2 VALUES (2,20)
    INSERT INTO MyCursor2 VALUES (3,30)
    INSERT INTO MyCursor2 VALUES (4,40)
    INSERT INTO MyCursor2 VALUES (5,50)
    INSERT INTO MyCursor2 VALUES (6,60)
    INSERT INTO MyCursor2 VALUES (7,70)
    INSERT INTO MyCursor2 VALUES (8,80)
    
    CREATE CURSOR MyCursor3 (field1 I , field2 I)
    INSERT INTO MyCursor3 VALUES (6,600)
    INSERT INTO MyCursor3 VALUES (7,700)
    
    DELETE MyCursor1 FROM MyCursor2  ;
      WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1
    
    SELECT MyCursor1
    LIST
    RECALL ALL
    
    DELETE MyCursor1 FROM MyCursor2, MyCursor3  ;
      WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 AND MyCursor2.field1=MyCursor3.field1
    
    SELECT MyCursor1
    LIST
    RECALL ALL
    
    DELETE FROM MyCursor1 WHERE MyCursor1.field1>5
    
    SELECT MyCursor1
    list
    RECALL ALL
    
    DELETE MyCursor1 from MyCursor1 WHERE MyCursor1.field1>5
    
    RECALL ALL IN MyCursor1
    
    DELETE T1 ;
      FROM MyCursor1 T1 JOIN MyCursor2 ON T1.field1>5 AND MyCursor2.field1=T1.field1, MyCursor3  ;
      WHERE MyCursor2.field1=MyCursor3.field1
    
    RECALL ALL IN MyCursor1
    
    

    Updatable Fields in UPDATE – SQL Command

    The number of fields that can be updated with the UPDATE – SQL Command is no longer limited to 128 as in prior versions of Visual FoxPro. You are now limited to 255, which is the number of fields available in a table.

    SET ENGINEBEHAVIOR

    The SET ENGINEBEHAVIOR Command has a new Visual FoxPro 9.0 option, 90, that affects SELECT – SQL command behavior for the TOP N clause and aggregate functions. For additional information, see the SET ENGINEBEHAVIOR Command.

    Data Type Conversion

    Conversion between data types (for example, conversion between memo and character fields) has been improved in Visual FoxPro 9.0. This conversion improvement applies to the ALTER TABLE – SQL Command with the COLUMN option as well as structural changes made with the Table Designer.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release
    ALTER TABLE – SQL Command
    SET SQLBUFFERING Command
    SET ENGINEBEHAVIOR Command

    Other Resources

    What’s New in Visual FoxPro

    Class Enhancements

    • Article
    • 07/09/2007

    In this article

    1. Anchoring Visual Controls
    2. Docking Forms
    3. CheckBox and OptionButton Controls Support Wordwrapping
    4. CommandButton Controls Can Align Text with Pictures

    Show 24 more

    Visual FoxPro contains the following enhancements to classes, forms, controls and object-oriented related syntax.

    Anchoring Visual Controls

    You can anchor a visual control to one or more edges of its parent container using the control’s Anchor property. When you anchor a visual control to the parent container, the edges of the control remain in the same position relative to the edges of the container when you resize the container. For more information, see Anchor Property.

    Docking Forms

    Visual FoxPro extends docking support to user-defined forms. Docking forms works similarly to docking toolbars except that you can dock forms to Visual FoxPro Interactive Development Environment (IDE) windows and other forms, and controls on the form can still obtain focus when the form is docked.

    Visual FoxPro includes the following new and updated properties, methods, and events to support docking forms.

    For more information, see How to: Dock Forms.

    CheckBox and OptionButton Controls Support Wordwrapping

    The WordWrap property is now supported for CheckBox and OptionButton controls. The text portions of these controls now use wordwrapping. For more information, see WordWrap Property.

    CommandButton Controls Can Align Text with Pictures

    The Alignment property now applies to CommandButton controls when specifying an image for the Picture property and setting the PicturePosition property to a value other than the default. The Alignment property also contains new and revised settings for CommandButtonCheckBox, and OptionButton controls. For more information, see Alignment Property.

    CommandButton, OptionButton, and CheckBox Controls Can Hide Captions

    The PicturePostion property contains a new setting of 14 (No text) for CommandButtonOptionButton, and CheckBox controls. You can use this setting to hide the text portions of these controls without needing to set the Caption property to an empty string. This setting is particularly useful when you want to include a hotkey for a button with a graphic without displaying the Caption text. You must set the Style property to 1 (Graphical) for this new setting to apply.

    In addition, the PicturePosition property now applies to CheckBox and OptionButton controls when Style is set to 1 (Graphical).

    For more information, see PicturePosition Property.

    PictureMargin and PictureSpacing Properties Control Spacing and Margins on CommandButton, OptionButton, and CheckBox Controls

    You can better control positioning of images on CommandButtonOptionButton, and CheckBox controls with the new PictureMargin and PictureSpacing properties. The PictureMargin property specifies margin spacing in pixels between an image and the control’s border as determined by the PicturePosition property. The PictureSpacing property specifies margin spacing in pixels between an image and text on the control.

    For more information, see PictureMargin Property and PictureSpacing Property.

    Collection Objects Support in ComboBox and ListBox Controls

    You can now specify Collection objects as the row source and row source type for the RowSource and RowSourceType properties of ComboBox and ListBox controls. For more information, see RowSource Property and RowSourceType Property.

    Setting Ascending or Descending Indexes on Cursors in the DataEnvironment

    You can specify ascending or descending order for a cursor index by using the new OrderDirection property for Cursor objects.

     Note

    OrderDirection is disregarded when the cursor’s Order property is empty.

    For more information, see OrderDirection Property.

    Grid Supports Rushmore Optimization

    The Grid Control can be set to support Rushmore optimization if the underlying data source contains indexes that support this.

    For more information, see Optimize Property.

    Mouse Pointer Control for Grid Columns and Column Headers

    The MousePointer and MouseIcon properties now apply to Column objects in a grid and Header objects in a column. For the MousePointer property, you can specify the new setting of 16 (Down Arrow) to reset the mouse pointer for a column header to the default down arrow.

    For more information, see MousePointer Property and MouseIcon Property.

    Rotating Label, Line, and Shape Controls

    You can use the new Rotation property to rotate Label controls. The Rotation property applies to Line and Shape controls when used with the new PolyPoints property. For more information, see Rotation Property (Visual FoxPro)PolyPoints Property, and Creating More Complex Shapes using the PolyPoints Property.

    Label Controls Can Display Themed Background

    For Label controls, you can set the Style property to Themed Background Only to show only themed background colors when Windows themes are turned on. The label background color is the same as the parent container for the label. For more information, see Style Property.

    ListBox Controls Can Hide Scroll Bars

    You can use the new AutoHideScrollBar property for ListBox controls to hide scroll bars when the list contains less than the number of items that can be visible in the list box. For more information, see AutoHideScrollBar Property.

    Toolbar Controls Can Display Horizontal Separator Objects

    For Separator objects, set the Style property to 1 to display a horizontal line or a vertical line, depending on how the toolbar appears. If the toolbar appears horizontally, the line displays vertically. If the toolbar appears vertically, the line displays horizontally. In versions prior to this release, setting Style to 1 displayed a vertical line only.

     Note

    In versions prior to this release, undocked vertical system and user-defined toolbars did not display horizontal separators. In the current release, horizontal separators now display for vertical undocked toolbars.

    For more information, see Style Property.

    Toolbar Controls Can Hide Separator Objects

    The Visible property now applies to Separator objects so you can control whether a Separator object displays in Toolbar controls. When used in combination with the Style property, the separator’s Visible property determines whether a space or line displays as the separator when its Style property is set to 0 (Normal – do not display a line) or 1 (display a horizontal or vertical line), respectively.

    For more information, see Visible Property (Visual FoxPro).

    Creating More Complex Shapes

    You can use the new PolyPoints property for Line and Shape controls to create polygon lines and shapes. PolyPoints specifies an array of any dimension containing coordinates with the format of X1, Y1, X2, Y2, …, organized in the order in which the polygon line or shape is drawn.

    For Line controls, when you create a polygon line using the PolyPoints property, you can specify the new setting of “S” or “s” for the LineSlant property to create a Bezier curve.

    For more information, see PolyPoints Property and LineSlant Property.

    ComboBox Controls Can Hide Drop-Down Lists

    You can now use the NODEFAULT command in the DropDown event for a ComboBox control. This will prevent the drop-down list portion of a ComboBox control from appearing. For more information, see NODEFAULT Command.

    NEWOBJECT( ) Creates Objects without Raising Initialization Code

    To mimic the behavior of a class opened in Class Designer or Form Designer, pass 0 to the cInApplication parameter. This feature allows you to create design-time tools that view the structure of a class.

    By passing 0 to the cInApplication parameter for the NEWOBJECT( ) function, Visual FoxPro allows you to create an instance of a class without raising initialization code (such as code in the InitLoadActivate, and BeforeOpenTables events). Furthermore, when the object is released, it does not raise its destructor code (such as code in the Destroy and Unload events). Only initialization and destructor code is suppressed; code in other events and methods is still called.

    If you use the cInApplication parameter to suppress initialization and destructor code in an object, you also suppress it in the object’s child objects.

    This behavior is not supported for the NewObject Method.

    For more information, see NEWOBJECT( ) Function.

    Specify Where Focus is Assigned in the Valid Event

    To direct where focus is assigned, you can use the optional ObjectName parameter in the RETURN command of the Valid event. The object specified must be a valid Visual FoxPro object. If the specified object is disabled or cannot receive focus, then focus is assigned to the next object in the tab order. If an invalid object is specified, Visual FoxPro keeps the focus at the current object.

    You can now set focus to objects in the following scenarios:

    • Set focus to an object on another visible form.
    • Set focus to an object on a non-visible Page or Pageframe control.

    For more information, see Valid Event.

    TextBox Controls Have Auto-Completion Functionality

    You can add auto-completion functionality to your text box controls to make data entry more efficient. Auto-completion is the automatic display of a drop-down list of entries that match the string as it is typed into the text box. The entries come from a special table that tracks unique values entered into the text box, the control name that is the source of the value, and usage information.

    The following properties support auto-completion:

    By the setting the AutoComplete property, you determine the sort order for the entries. If you want more control over the list and where it is stored, you can use the AutoCompSource property to specify the table used to populate the automatic list. By default, the table is AUTOCOMP.DBF. You can use one table for each text box control or a single table can populate automatic lists for several text boxes.

    If you use a single table, which is the default, the table uses values in the Source field for each entry to identify the text box control associated with the entry. By default, the Source field value is the name of the text box control. You can specify the Source field value using the AutoCompSource property of the text box. For example, you might want to make the same set of entries available to multiple Text box controls within the application such as address information. You can explicitly set the AutoCompTable and AutoCompSource properties for each of the controls to the same table and source field value. The same automatic list appears for each of them.

    The text box control handles updating the auto-completion table for you based on the values actually entered in the text box. If you want to remove a value from the list, enter a string in a text box that matches the string you want to delete to filter the list for it. Select the entry in the list and press the DELETE key. The string remains in the table but no longer appears in the automatic list.

     Note

    You can control the number of items that appear in the drop-down list using SYS(2910) – List Display Count.

    For more information, see AutoComplete PropertyAutoCompSource Property, and AutoCompTable Property.

    New InputMask and Format Property Settings

    The following new InputMask and Format settings are available:

    InputMask PropertyExpand table

    cMaskDescription
    UPermits alphabetic characters only and converts them to uppercase (A – Z).
    WPermits alphabetic characters only and converts them to lowercase (a – z).

    Format PropertyExpand table

    cFunctionDescription
    ZDisplays the value as blank if it is 0, except when the control has focus.Dates and DateTimes are also supported in these controls. The date and datetime delimiters are not displayed unless the control has focus.

    For more information, see InputMask Property and Format Property.

    Use PictureVal Property to Pass Images as Strings

    The Image control’s new PictureVal property can be used instead of the Picture Property (Visual FoxPro) to specify a character string expression or object of an image. For an object, the format must be of an IPicture interface format compatible with LOADPICTURE( ) Function.

    For more information, see PictureVal Property.

    CLEAR CLASSLIB Updated

    The CLEAR CLASSLIB command now automatically executes a CLEAR CLASS command on each class in the specified class library. Any errors that might occur during release of individual classes (e.g., class in use) are ignored.

     Note

    Classes in other class libraries that are used or referenced by a class in the specified class library are not cleared.

    For more information, see CLEAR Commands.

    Screen Resolution Limit Increased

    In prior versions of Visual FoxPro, the definable maximum area for a form is limited to twice the Screen Resolution for both X and Y dimensions. For example, if your monitor resolution is 1280×1024, then the max dimensions would be:Copy

    Form.Width = 2552
    Form.Height = 2014
    
    

    Additionally, if you attempted to set Width and Height properties to these limits in design-time and then ran the form, you would see that the values have reverted to screen resolution limits (being that they were saved this way):Copy

    Form.Width = 1280
    Form.Height = 998
    
    

    In Visual FoxPro 9.0, this limitation has been increased to approximately 32,000 pixels for each dimension and now allows for more flexibility with certain forms such as scrollable ones:Copy

    Form.Width = 32759
    Form.Height = 32733
    
    

    For more information, see Width Property and Height Property.

    ProjectHook Source Code Control Events

    New events have been added to the ProjectHook class, which allow you to perform source code control operations such as check-in and check-out of multiple files at once.

    For more information, see SCCInit Event and SCCDestroy Event.

    AddProperty Method Supports Design Time Settings

    You can specify the visibility (ProtectedHidden or Public) and description of a property using the AddProperty method with new available parameters. These settings can also be controlled from the New Property Dialog Box and Edit Property/Method Dialog Box. For more information, see AddProperty Method.

    WriteMethod Method Supports Design Time Settings

    You can specify the visibility (ProtectedHidden or Public) and description of a method using the WriteMethod method with new available parameters. These settings can also be controlled from the New Property Dialog Box and Edit Property/Method Dialog Box. For more information, see WriteMethod Method.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Language Enhancements

    • Article
    • 07/09/2007

    In this article

    1. Class Enhancements
    2. Data and XML Enhancements
    3. IDE Enhancements
    4. Printing and Reporting Enhancements

    Show 32 more

    In the current release of Visual FoxPro, you will find enhanced functionality via new and enhanced commands and functions. Expand and collapse this topic to see information about miscellaneous language additions and improvements.

    Class Enhancements

    Visual FoxPro contains significant language enhancements for classes, forms, controls, and object-oriented related features. For more information, see Class Enhancements.

    Data and XML Enhancements

    Visual FoxPro contains significant language enhancements for Data, SQL and XML features. For more information, see SQL Language Improvements and Data and XML Feature Enhancements.

    IDE Enhancements

    Visual FoxPro contains a number of language enhancements for features related to the IDE (Interactive Development Environment). For more information, see Interactive Development Environment (IDE) Enhancements and Enhancements to Visual FoxPro Designers.

    Printing and Reporting Enhancements

    Visual FoxPro contains a number of language enhancements to support new Reporting functionality:

    Additionally, there are improvements to the following related Printing language elements:

    • SYS(1037) – Page Setup Dialog BoxDisplays Visual FoxPro default or report Page Setup dialog box or sets printer settings for the default printer in Visual FoxPro or for the report printer environment. In this version, a new nValue parameter is available.
    • APRINTERS( ) FunctionReturns a five-column array with the name of the printer, connected port, driver, comment, and location. The last three columns are available if the new optional parameter is passed.
    • GETFONT( ) FunctionContains an additional setting to display only those fonts available on the current default printer and clarified values for the language script.

    New Reporting functionality is described in more detail in separate Reporting topics. For more information, see Guide to Reporting Improvements.

    Specifying Arrays with More Than 65K Elements

    You can now specify arrays containing more than 65,000 elements, for example, when using the DIMENSION command. Normal arrays and member arrays have a new limit of 2GB. Arrays containing member objects retain a limit of 65,000 elements.

     Note

    Array sizes can also be limited by available memory, which affects performance, especially for very large arrays. Make sure your computer has enough memory to accommodate the upper limits of your arrays.

    The Library Construction Kit, which contains the files Pro_Ext.h, WinAPIMS.lib, and OcxAPI.lib, still has a limit of 65,000 elements. For more information about these files, see Accessing the Visual FoxPro APIHow to: Add Visual FoxPro API Calls, and How to: Build and Debug Libraries and ActiveX Controls. The SAVE TO command does not support saving arrays larger than 65,000 elements.

    For more information, see Visual FoxPro System Capacities and DIMENSION Command.

    STACKSIZE Setting Increases Nesting Levels to 64k

    For operations such as the DO command, you can change the default number of nesting levels from 128 levels to 32 and up to 64,000 levels of nesting by including the new STACKSIZE setting in a Visual FoxPro configuration file.

     Note

    You can change the nesting level only during Visual FoxPro startup.

    For more information, see Special Terms for Configuration Files and Visual FoxPro System Capacities.

    Program and Procedure File Size Is Unrestricted

    In previous versions of Visual FoxPro, the size of a procedure or program could not exceed 65K. Visual FoxPro now removes this restriction for programs and procedures. For more information, see Visual FoxPro System Capacities.

    PROGCACHE Configuration File Setting

    In previous versions of Visual FoxPro, you could not specify the program cache size or amount of memory reserved to run programs. This configuration file setting allows you to control this. It is especially useful for MTDLL scenarios. For more information, see Special Terms for Configuration Files.

    ICASE( ) Function

    The new ICASE( ) function makes it possible for you to evaluate a list of conditions and return results depending on the result of evaluating those conditions. For more information, see ICASE( ) Function.

    TTOC( ) Converts DateTime Expressions to XML DateTime Format

    You can convert a DateTime expression into a character string in XML DateTime format by passing a new optional value of 3 to the TTOC( ) function. For more information, see TTOC( ) Function.

    SET COVERAGE Command Available at Run Time

    The SET COVERAGE command is now available at run time so that you can debug errors that occur at run time but not at design time. For more information, see SET COVERAGE Command.

    CLEAR ERROR Command

    The new ERROR clause for the CLEAR command makes it possible to reset the error structures as if no error occurred. This affects the following functions:

    • The AERROR( ) function will return 0.
    • The ERROR( ) function will return 0.
    • The output from MESSAGE( )MESSAGE(1) and SYS(2018) will return a blank string.

    The CLEAR command should not be used with the ERROR clause within a TRY…CATCH…FINALLY structure. For more information, see CLEAR Commands.

    Write Options Dialog Settings to Registry Using SYS(3056)

    The SYS(3056) function can now be used to write out settings from the Options dialog box to the registry.

    SYS(3056 [, nValue ])

    The following table lists values for nValue.Expand table

    nValueDescription
    1Update only from registry settings, with the exception of SET commands and file locations.
    2Write out settings to the registry.

    For more information, see SYS(3056) – Read Registry Settings.

    FOR EACH … ENDFOR Command Preserves Object Types

    Visual FoxPro now includes the FOXOBJECT keyword for the FOR EACH … ENDFOR command to support preservation of native Visual FoxPro object types.

    FOR EACH objectVar [AS Type [OF ClassLibrary ]] IN Group FOXOBJECT

    Commands

    [EXIT]

    [LOOP]

    ENDFOR | NEXT [Var]

    The FOXOBJECT keyword specifies that the objectVar parameter created will be a Visual FoxPro object. The FOXOBJECT keyword only applies to collections where the collection is based on a native Visual FoxPro Collection class. Collections that are COM-based will not support the FOXOBJECT keyword.

    For more information, see FOR EACH … ENDFOR Command.

    SET PATH Command Enhancements

    The SET PATH command now supports the ADDITIVE keyword. The ADDITIVE keyword appends the specified path to the end of the current SET PATH list. If the path already exists in the SET PATH list, Visual FoxPro does not add it or change the order of the list. Paths specified with the ADDITIVE keyword must be strings in quotes or valid expressions.

    In addition, the length of the SET PATH list has been increased to 4095 characters.

    For more information, see SET PATH Command.

    Trim Functions Control Which Characters Are Trimmed

    It is now possible to specify which characters are trimmed from an expression when using the TRIM( )LTRIM( )RTRIM( ), and ALLTRIM( ) functions.

    TRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, ...]]])

    LTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, ...]]])

    RTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, ...]]])

    ALLTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, ...]]])

    You can specify that the trim is case-insensitive using the nFlag value of 0 bit and 1.

    The cParseChar parameter specifies one or more character strings to be trimmed from cExpression. A maximum of 23 strings can be specified in cParseChar.

    By default, if cParseChar is not specified, then leading and trailing spaces are trimmed from character strings or 0 bytes are removed for Varbinary data types.

    The cParseChar parameters are applied in the order they are entered. When a match is found, cExpression is truncated and the process repeats from the first cParseChar parameter.

    For more information, see the TRIM( ) FunctionLTRIM( ) FunctionRTRIM( ) Function, and ALLTRIM( ) Function topics.

    ALINES( ) Offers More Flexible Parsing Options

    The ALINES( ) function has been enhanced to provide several additional options such as case-insensitive parsing and improved handling of empty array elements. These options are available using the new nFlags parameter that replaces the older lTrim 3rd parameter. For more information, see ALINES( ) Function.

    Improvements in TEXT…ENDTEXT Statement

    You can use the TEXT…ENDTEXT command to eliminate line feeds using the new PRETEXT setting. A new FLAGS parameter controls additional output settings. For more information, see TEXT … ENDTEXT Command.

    Include Delimiters in STREXTRACT( ) Results

    The STREXTRACT( ) function has a new nFlags setting that allows you to include the specified delimiters with the returned expression. For more information, see STREXTRACT( ) Function.

    STRCONV( ) Enhanced to Allow for Code Page and FontCharSet

    For certain conversion settings, you can specify an optional Code Page or Fontcharset setting for use in the conversion. For more information, see STRCONV( ) Function.

    TYPE( ) Determines if an Expression is an Array

    The TYPE( ) function accepts the parameter, 1, to evaluate an expression to determine if it is an array.

    Type(cExpression, 1)

    The following character values are returned if the 1 parameter is specified.Expand table

    Return ValueDescription
    AcExpression is an array.
    UcExpression is not an array.
    CcExpression is a collection.

    cExpression must be passed as a character string.

    For more information, see TYPE( ) Function.

    BINTOC( ) and CTOBIN( ) Have Additional Conversion Capabilities

    The BINTOC( ) and CTOBIN( ) functions have update or new parameters that provide you with more control over the output of these functions. Additionally, these enhancements offer some improvements for working with Win32 API routines. For more information, see BINTOC( ) Function and CTOBIN( ) Function.

    MROW( ) and MCOL( ) Can Detect the Position of the Mouse Pointer

    The MROW( ) and MCOL( ) functions now have a zero (0) parameter for detecting the position of the mouse pointer based on the currently active form instead of the form returned by the WOUTPUT( ) function. Although they are typically reference the same form, if the AllowOutput property of the form is set to False (.F.), WOUTPUT( ) does not return the current active form. The mismatch of references and can lead to unexpected results. By using the zero (0) parameter, you can avoid misplacing items, such as Shortcut menus, as the currently active form is always used.

    For more information, see MROW( ) Function and MCOL( ) Function.

    INPUTBOX( ) Returns A Cancel Operation

    The INPUTBOX( ) function contains an additional parameter that allows you to determine if the user canceled out of the dialog. For more information, see INPUTBOX( ) Function.

    AGETCLASS( ) Supported for Runtime Applications

    The AGETCLASS( ) fiunction is now supported for runtime applications. For more information, see AGETCLASS( ) Function.

    SYS(2019) Extends Handling of Configuration Files

    You can use SYS(2019) to obtain the name and location of both internal and external configuration files. For more information, see SYS(2019) – Configuration File Name and Location.

    SYS(2910) Controls List Display Count

    You can control the number of items that appear in a drop-down list such as the one used by AutoComplete Property. This is the setting that is available on the View Tab, Options Dialog Box of the Options Dialog Box (Visual FoxPro).

    For more information, see SYS(2910) – List Display Count.

    Visual FoxPro will display a tip such as “CTRL+Click to follow the link” when you hover over a hyperlink in the editor. If you desire to not have this tip show, you can use SYS(3008) to turn it off. This function is also useful for international applications where you do not want to display the English text for this tip. For more information, see SYS(3008) – Hyperlink Tooltips.

    SYS(3065) Internal Program Cache

    You can obtain the internal program cache (PROGCACHE configuration file setting). For more information, see SYS(3065) – Internal Program Cache.

    SYS(3101) COM Code Page Translation

    You can now specify a code page to use for character data translation involving COM interoperability. For more information, see SYS(3101) – COM Code Page Translation.

    Bidirectional Support for Tooltips and Popups

    For international applications that display text from right to left, you can use the following new enhancements to control text justification:

    • SYS(3009) – right justifies text in ToolTips.
    • DEFINE POPUP…RTLJUSTIFY – right justifies items in a popup, such as a shortcut menu.
    • SET SYSMENU TO RTLJUSTIFY – right justifies an entire menu system.

    The SYS(3009) function is a global setting. For more information, see SYS(3009) – Bidirectional Text Justification for ToolTipsDEFINE POPUP Command and SET SYSMENU Command.

    Enhanced Font Script Support

    Visual FoxPro 9.0 contains a number of enhancements that extend ability to specify a Font Language Script (or FontCharSet) along with existing Font settings:

    • SYS(3007) – specifies a FontCharSet for ToolTips. This is a global setting.
    • FONT Clause – the following table lists commands that support an optional FONT clause that allows for specification of a FontCharSet in the following format:FONT cFontName [, nFontSize [, nFontCharSet]]Expand tableCommandDEFINE MENUDEFINE POPUPDEFINE BARDEFINE PADDEFINE WINDOWMODIFY WINDOWBROWSE/EDIT/CHANGE?/??
    • Browse – the Font Dialog Box that you can invoke by selecting the Font menu item from the Table menu with a Browse Window active now allows for selection of a font language script. You can specify a global default font script from the IDE Tab, Options Dialog Box in the Options Dialog Box (Visual FoxPro). To do this, you must first check the Use font script checkbox.
    • Editors – the Font Dialog Box that you can invoke with an editor window active by selecting the Font menu item from the Format menu or right-click shortcut menu Edit Properties Dialog Box now allows for selection of a font language script. You can specify a global default font script from the IDE Tab, Options Dialog Box in the Options Dialog Box (Visual FoxPro). To do this, you must first check the Use font script checkbox.

    For more information, see SYS(3007) – ToolTipText Property Font Language ScriptIDE Tab, Options Dialog Box, and FontCharSet Property.

    ToolTip Timeout Control

    You can specify how long a ToolTip is displayed if the mouse pointer is left stationary. For more information, see _TOOLTIPTIMEOUT System Variable.

    Tablet PC Features

    The following features are available to assist with applications designed to run on a Tablet PC computer.

    • ISPEN( ) – determines if the last Visual FoxPro application mouse event on a Tablet PC was a pen tap.
    • _SCREEN.DisplayOrientation – this read-write property specifies the screen display orientation for a Tablet PC. The value returned is the current orientation.
    • _TOOLTIPTIMEOUT – specifies how long a ToolTip is displayed if the mouse pointer is left stationary.

    For more information, see ISPEN( ) FunctionDisplayOrientation Property, and _TOOLTIPTIMEOUT System Variable.

    Windows Message Event Handling

    Visual FoxPro allows you to trap and handle window messages from the Microsoft® Windows® operating system using existing BINDEVENT functions. Some examples of common events you might wish to trap for include:

    • A power broadcast message used to intercept standby or power-down activities.
    • Media insertion and removal events, such as the insertion of a CD into a drive.
    • The insertion and/or removal of a Plug and Play hard disk (e.g., USB Drive).
    • Interception of screen saver queries to stop the screen saver from activating.
    • Operating system level font changes and Windows XP Theme changes.
    • New network connections/shares added or removed from system.
    • Switching between applications.

    You can use the Visual FoxPro BINDEVENT functions to register (and unregister) event handlers used to intercept messages (i.e., Win32 API window messages that get processed by the Win32 WindowProc function). See MSDN for more details.

    The new BINDEVENT( ) syntax requires the hWnd (integer) of the window receiving the message you desire to intercept, and the specific message itself (integer). For example, power-management events such as standby and power-down use the Win32 WM_POWERBROADCAST message (value of 536).

    BINDEVENT(hWnd, nMessage, oEventHandler, cDelegate)

    The following example illustrates detection of a Windows XP Theme change:Copy

    #DEFINE WM_THEMECHANGED    0x031A
    #DEFINE GWL_WNDPROC    (-4)
    PUBLIC oHandler
    oHandler=CREATEOBJECT("AppState")
    BINDEVENT(_SCREEN.hWnd, WM_THEMECHANGED, oHandler, "HandleEvent")
    MESSAGEBOX("Test by changing Themes.")
    DEFINE CLASS AppState AS Custom
    nOldProc=0
    PROCEDURE Destroy
        UNBINDEVENT(_SCREEN.hWnd, WM_THEMECHANGED)
    ENDPROC
    PROCEDURE Init
        DECLARE integer GetWindowLong IN WIN32API ;
            integer hWnd, ;
            integer nIndex
        DECLARE integer CallWindowProc IN WIN32API ;
            integer lpPrevWndFunc, ;
            integer hWnd,integer Msg,;
            integer wParam,;
            integer lParam
        THIS.nOldProc=GetWindowLong(_VFP.HWnd,GWL_WNDPROC)
    ENDPROC
    PROCEDURE HandleEvent(hWnd as Integer, Msg as Integer, ;
        wParam as Integer, lParam as Integer)
        lResult=0
        IF msg=WM_THEMECHANGED
            MESSAGEBOX("Theme changed...")
        ENDIF
        lResult=CallWindowProc(this.nOldProc,hWnd,msg,wParam,lParam)
        RETURN lResult
    ENDPROC
    ENDDEFINE
    
    

    The following SYS( ) functions are also available to assist with handing these events:

    • SYS(2325) – returns the hWnd of a client window from the parent window’s WHANDLE.
    • SYS(2326) – returns a Visual FoxPro WHANDLE from a window’s hWnd.
    • SYS(2327) – returns a window’s hWnd from a Visual FoxPro window’s WHANDLE.

    For more information, see BINDEVENT( ) FunctionUNBINDEVENTS( ) Function, and AEVENTS( ) Function. Also, see SYS(2325) – WCLIENTWINDOW from Visual FoxPro WHANDLESYS(2326) – WHANDLE from a Window’s hWnd, and SYS(2327) – Window’s hWnd from Visual FoxPro WHANDLE for related topics. Refer to MSDN as reference source for details on specific window messages.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Interactive Development Environment (IDE) Enhancements

    • Article
    • 07/09/2007

    In this article

    1. Additional Project Manager Shortcut Menu Commands
    2. Modifying a Class Library from the Project Manager
    3. Set Font of Project Manager
    4. Generating Message Logs During Project Build and Compile

    Show 15 more

    To provide a more integrated development environment for your projects and applications, Visual FoxPro contains the following improved functionality for the IDE.

    Additional Project Manager Shortcut Menu Commands

    When docked, the Project Manager window contains the following additional shortcut menu commands that are available on the Project menu:

    • CloseCloses the Project Manager.
    • Add Project to Source ControlCreates a new source control project based on the current project. Available only when a source code control provider is installed and specified on the Projects tab in the Options dialog box.
    • ErrorsDisplays the error (.err) file after running a build.
    • RefreshRefreshes the contents of the Project Manager.
    • Clean Up ProjectRemoves deleted records from the Project Manager (.PJX) file.

    Modifying a Class Library from the Project Manager

    When you select a class library (.vcx) file in the Project Manager, you can now open and browse class libraries by clicking the Modify button. The class library opens in the Class Browser. For more information, see How to: Open Class Libraries.

    Set Font of Project Manager

    You can change the text font settings for the Project Manager window. Right-click the Project Manager window (outside of the tree hierarchy window) and choose Font.

    Generating Message Logs During Project Build and Compile

    When you build a project, application, or dynamic-link library, Visual FoxPro automatically generates an error (.err) file that includes any error messages, if they exist, when the build process completes. When you select the Display Errors check box in the Build Options dialog box, Visual FoxPro displays the .err file when the build completes. Selecting the Recompile All Files check box includes compile errors in the .err file. Build status messages usually appear in the status bar. However, in previous versions, if the build process is interrupted, Visual FoxPro did not write the .err file to disk.

    In the current release, Visual FoxPro writes build status and error messages to the .err file as they occur during the build process. If the build process is interrupted, you can open the .err file opens to review the errors.

     Note

    If no errors occur during the build, the .err file is deleted.

    If the Debug Output window is open, build status and error messages appear in the window. You can save messages from the Debug Output window to a file.

    For more information, see How to: View and Save Build Messages.

    Properties Window Enhancements

    • Design time support for entering property values greater than 255 characters and extended characters, such as CHR(13) (carriage return) and CHR(10) (linefeed), has been added to visual class library (.vcx) and form (.scx) files. You can now enter up to 8k characters in length. NoteExtended property value support is only available through the Properties Window (Zoom dialog box) for custom user-specified properties as well as certain native ones such as CursorSchema and Value. For properties not supported, you can still specify values which are longer than 255 characters, or contain carriage returns and linefeeds by assigning them in code such as during the object’s Init Event.The Zoom dialog box and Expression Builder dialog box have been updated to support this. The Properties window includes a Zoom (Z) button that appears next to the property settings box for appropriate properties. WarningProperty values that exceed 255 characters or include carriage return and/or linefeed characters are stored in a new format inside the .vcx or .scx file. If you attempt to modify these classes in a prior version, an error occurs.This feature is particularly useful for setting the CursorAdapter CursorSchema property to any schema expression when schemas might exceed 255 characters.
    • The Properties window font can now be specified by the new Font shortcut menu option. This new menu replaces the Small, Medium and Large font menu items used in prior versions. This font is also used in the description pane, and object and property value dropdowns. NoteBold and italic font styles are reserved for non-default property values and read-only properties, respectively. If a bold or italic font style is chosen, then the Properties window inverts the displayed behavior. For example, if one chooses an italic font style, read-only properties appear in normal font style and all others in italic.
    • Colors can be specified for certain types of properties by right clicking on the Properties Window and selecting following menu items:
      • Non-Default Properties ColorSets color for properties whose values have changed from default setting (same properties that are displayed when the Non-Default Properties Only menu item is selected).
      • Custom Properties ColorSets color for custom properties.
      • Instance Properties ColorSets color for custom properties that have been added to the current class instance (same properties that appear in bold in the Edit Property/Method Dialog Box).
       NoteIf a conflict exists between color settings, the Instance setting takes priority followed by the Non-Default one.

    For more information, see Zoom <property> Dialog BoxExpression Builder Dialog BoxCursorSchema Property, and Properties Window (Visual FoxPro).

    MemberData Extensibility

    The MemberData extensibility architecture lets you provide metadata for class members (properties, methods and events). With MemberData, you can specify a custom property editor, display a property on the Favorites tab, or change the capitalization in the Properties Window (Visual FoxPro).

    For more information, see MemberData Extensibility.

    Setting Default Values for New Properties

    When adding a new property to a class, you can specify an initial value other than the default in the New Property dialog box. Subclasses inherit these default values unless you reset the default values to the parent class. In previous versions, you had to set the default value for the new property by selecting the property in the Properties window and setting the default value.

    For more information, see How to: Add Properties to Classes.

    Document View Sort Options

    You can now sort items in the Document View window by name for forms and visual class libraries.

    See Document View Window for more information on sorting items in the Document View Window.

    Compiling Code in the Background

    Visual FoxPro performs background compilation when syntax coloring is turned on in the Command window and Visual FoxPro editors for program (.prg) files, methods, stored procedures, and memos. The Expression box in the Expression Builder dialog box also includes support for background compilation and syntax coloring when turned on.

    When the single and current line of code that you are typing contains invalid syntax, Visual FoxPro displays the line of code with the formatting style selected in the Editor tab of the Options dialog box.

     Note

    Syntax coloring must be turned on for background compilation to function. Background compilation does not detect invalid syntax in multiple lines of code, including those containing continuation characters.

    For more information, see How to: Display and Print Source Code in Color.

    Rich Text Format (RTF) Clipboard Support

    Visual FoxPro now supports copying in RTF (Rich Text Format) to the clipboard. Visual FoxPro preserves the style (bold, italic, and underline) and color attributes.

    RTF is supported only in the FoxPro editors that allow for syntax coloring, such as the Command window and editing windows opened with MODIFY COMMAND Command. The RTF clipboard format is only supported when syntax coloring is enabled such as from Edit Properties Dialog Box. You can disable RTF clipboard format with the _VFP EditorOptions Property.

    The _CLIPTEXT System Variable does not support RTF.

    Find Dialog Box Improvements

    The following improvements were made to Find support:

    • If a word is selected in a Visual FoxPro editor, the Find Dialog Box (Visual FoxPro) when opened now displays the word in the Look For drop-down box. If Find has not yet been used for a running instance of Visual FoxPro, a word positioned under the insertion pointer will appear in the Look For drop-down. If multiple words are selected, only the first word appears in the drop-down (use copy and paste to enter multiple words).
    • When a Browse window is open and you search for a word with the Find dialog box, you can search for the word again (Find Again) after the Find dialog box is closed by pressing the F3 key.
    • You can now use Find to search for content in Name column of the Watch and Locals debug windows (see Debugger Window). When searching object members, Find searches in these debug windows are limited to nodes that have been expanded and one level below.

    View Constants in Trace Window

    Constants (#DEFINE values) can be viewed in the Trace Window when you hover over it with the mouse.

     Note

    Visual FoxPro evaluates constants as expressions in the Trace Window and may have difficulty interpreting a specific #DEFINE when you hover over it with the mouse. Consequently, if there are multiple expressions on a line, they are all displayed in the value tip.

    Printing Selected Text in Editor Windows

    You can print selected text from Visual FoxPro editor windows. When you have text selected in the editor window, the Selection option in the Print dialog box is available and selected.

     Note

    If a partial line is selected, the entire line is printed.

    For more information, see Print Dialog Box (Visual FoxPro).

    System Font Improvements

    To improve legibility on high-resolution monitors, Error dialog boxes and the Zoom <property> Dialog Box in the Properties window now use the Windows Message Box text font.

    In Windows XP, the Windows Message Box text font is set by opening Display in the Control Panel, and then clicking Advanced on the Appearance tab.

    IntelliSense Saves Settings Between User Sessions

    Visual FoxPro now saves IntelliSense settings, such as turning IntelliSense on, between user sessions. These settings are controlled by the _VFP EditorOptions property. In addition, the settings in the _VFP EditorOptions property are saved in the FoxUser.dbf resource file. For more information, see EditorOptions Property.

    IntelliSense in Memo Field Editor Window

    Visual FoxPro includes IntelliSense support in Memo field editor windows when syntax coloring is turned on.

    IntelliSense Available for Runtime Applications

    Selected IntelliSense features are available at run time in distributed Visual FoxPro 9.0 applications. In order to use IntelliSense at run time, you need to set the _FOXCODE and _CODESENSE variables, and EditorOptions Property.

     Note

    With runtime applications, syntax coloring does not need to be turned on for an editor to support IntelliSense.

    For more information, see IntelliSense Support in Visual FoxPro_FOXCODE System Variable_CODESENSE System Variable and EditorOptions Property.

    IntelliSense Support in WITH … ENDWITH and FOR EACH … ENDFOR Commands

    Visual FoxPro now supports IntelliSense within the WITH … ENDWITH Command and FOR EACH … ENDFOR Command.

    WITH ObjectName [AS Type [OF ClassLibrary]]

      Commands

    ENDWITH

    FOR EACH ObjectName [AS Type [OF ClassLibrary]] IN Group

      Commands

      [EXIT]

      [LOOP]

    ENDFOR

    The Type parameter can be any valid type, including data types, class types, or ProgID. If the class name cannot be found, Visual FoxPro disregards Type and does not display IntelliSense for it.

     Note

    The type reference does not affect the functionality of the application at run time. The type reference is only used for IntelliSense.

    The ObjectName expression can refer to a memory variable or an array.

    The ClassLibrary parameter must be in a path list that is visible to Visual FoxPro. You must specify a valid class library; references to existing objects are not valid. If Visual FoxPro cannot find the specified class library, IntelliSense does not display.

    Types expressed as ProgIDs and class libraries do not require quotation marks (“”) to enclose them unless their names contain spaces.

    When a user types the AS keyword, IntelliSense displays a list of types registered in the FoxCode.dbf table with Type “T”. If you have specified a valid type, typing a period within a WITH … ENDWITH or a FOR EACH … ENDFOR command displays IntelliSense for that object reference.

    Visual FoxPro supports IntelliSense for nested WITH … ENDWITH and FOR EACH … ENDFOR commands. The following is an example of nested WITH … ENDWITH commands in a class defined in a program (.prg) file named Program1.prg. To use, paste this code into a new program named Program1.prg, save it and then type a period (.) inside the WITH … ENDWITH block.Copy

    DEFINE CLASS f1 AS form
    MyVar1 = 123
    ADD OBJECT t1 AS mytext
    PROCEDURE Init
      WITH THIS AS f1 OF program1.prg
        WITH .t1 AS mytext OF program1.prg
        ENDWITH
      ENDWITH
    ENDPROC
    ENDDEFINE
    
    DEFINE CLASS mytext as textbox
    MyVar2 = 123
    ENDDEFINE
    
    

    IntelliSense provides limited List Values functionality for selected properties that begin with a “T” or “F” within a WITH … ENDWITH or FOR EACH … ENDFOR command. This is done to avoid possible conflicts with the common property values True (.T.) and False (.F.). If you just type “.T” or “.F” and press Enter, the word selected in the List Value drop-down does not expand. You need to type at least two letters for IntelliSense to insert the selected word.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Enhancements to Visual FoxPro Designers

    • Article
    • 07/09/2007

    In this article

    1. Report and Label Designers
    2. Menu Designer
    3. Table Designer
    4. Query and View Designers

    Show 3 more

    You might want to open the following designers and find the enhancements.

    Report and Label Designers

    You can use the Report Builder available in the Report Designer and Label Designer to perform reporting tasks, configure settings, and set properties for reporting features such as report layout, report bands, data groups, report controls, and report variables. For example, you can perform the following tasks:

    • Prevent users from modifying reports, report controls, and report bands when editing the report in protected mode.
    • Display captions instead of expressions for Field controls at design time.
    • Display user-defined ToolTips for report controls.
    • Set the language script for reports.
    • Save the report data environment as a class.

    By default, the Report Builder activates when you interact with the Report and Label designers. However, you can use the _REPORTBUILDER system variable to specify ReportBuilder.app. The Report Builder consolidates, replaces, and adds to the functionality found in previous Report Designer user interface elements, which remain in the product and are available by setting _REPORTBUILDER. You can write custom report builders to extend reporting functionality and output or run reports with report objects. For more information, see Working with Reports and _REPORTBUILDER System Variable.

    You can set the _MENUDESIGNER system variable to call your own custom designer for creating menus.Copy

    _MENUDESIGNER = cProgramName
    
    

    For more information, see _MENUDESIGNER System Variable.

    Table Designer

    The Table Designer accommodates the following data enhancements:

    Query and View Designers

    You can use spaces in table names specified in SQL statements in the Query and View designers if you provide an alias. For example, editing the following statement is valid in the View and Query designers:Copy

    SELECT * from dbo."Order Details" Order_Details
    
    

    For more information, see SELECT – SQL Command.

    Data Environment Designer

    The full path to the database (DBC) appears in the status bar when you select a database in the Add Table or View Dialog Box.

    Class and Form Designers

    The name of the class you are modifying appears in the title bar for the following dialog boxes:

    The View menu for the Form Designer offers both options for specifying the tab order on forms: Assign Interactively or Assign by List.

    In the ClassForm, and Report designers, you can use the following keyboard shortcut commands to adjust spacing between selected items.Expand table

    ShortcutDescription
    ALT+Arrow KeyAdjusts the spacing between the selected objects by one pixel in the direction of the arrow key.
    ALT+CTRL+Arrow KeyAdjusts the spacing between the selected objects by one grid scale in the direction of the arrow key.

    For more information, see Interactive Development Environment (IDE) Enhancements.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Miscellaneous Enhancements
    Changes in Functionality for the Current Release

    Miscellaneous Enhancements

    • Article
    • 07/09/2007

    In this article

    1. Printing Dialog Boxes and Printing Language Enhancements
    2. Improved Support for Applications Detecting Terminal Servers
    3. Updated Dr. Watson Error Reporting to 2.0
    4. Anchor Editor Application

    Show 11 more

    Visual FoxPro contains the following miscellaneous enhancements. You can click Collapse All to view a list of enhancments.

    Printing Dialog Boxes and Printing Language Enhancements

    Visual FoxPro includes various enhancements for its printing dialog boxes and printing language.

    Visual FoxPro uses the latest operating system dialogs for Printer Setup and other related printing operations. If the user is running on Windows XP, the dialogs will appear Themed.

    The following language functions contain new enhancements that impact general printing operations:

    For more information, see Language Enhancements.

    Improved Support for Applications Detecting Terminal Servers

    Visual FoxPro now automatically includes support for applications that are generated by the build process to detect whether they are running on a Terminal Server and prevent loading of unnecessary dynamic-link library (.dll) files that can impact performance. For more information, see BUILD EXE Command.

    Updated Dr. Watson Error Reporting to 2.0

    Visual FoxPro includes and updates its product error reporting to support Dr. Watson 2.0. This version includes new and improved error reporting, logging, and auditing features. For example, errors are logged while offline and are posted when you reconnect.

    Anchor Editor Application

    Visual FoxPro 9.0 allows you to create a custom property editor through extended metadata attributes for class members. Through this new extensibility model, you now have the ability to extend the functionality of class properties and methods, allowing you to create design-time enhancements such as a custom property editor. For more information about creating custom property editors, see MemberData Extensibility.

    A sample custom property editor, Anchoreditor.app, is included in Visual FoxPro 9.0 and is located in the Wizards directory. This application is run when the Anchor property is double-clicked in the Properties window, or by choosing the Anchor property in the Properties window and clicking the ellipsis button (…).Expand table

    TermDefinition
    Anchor but do not resize verticallySpecifies that the center of the control is anchored to the top and bottom edges of its container but the control does not resize.
    Anchor but do not resize horizontallySpecifies that the center of the control is anchored to the left and right edges of its container but the control does not resize.
    Border valuesDisplays the current settings for the border values.
    Common settingsSelects commonly used settings for the Anchor property.
    SampleClick the Sample button to test the current anchor value on a sample form.
    Anchor valueThe Anchor property value that is the combination of the current settings for the border values.

    Class Browser

    You can open and view class definitions that are specified within a program (.prg) similarly to class libraries (.vcx). You can select a program (.prg) from the File Open/Add dialog box. See Class Browser Window for more information.

    CursorAdapter Builder

    The CursorAdapter Builder contains a number of enhancements that correspond to improvements added to the CursorAdapter class. See CursorAdapter Builder for more information.

    Toolbox

    The Toolbox (Visual FoxPro) is now dockable and can be docked to the desktop or other IDE windows.

    Code References

    The Code References Window has been updated with the following minor enhancements:

    • For the results grid, the Options dialog provides a new setting to show separate columns for class, method, and line, rather than concatenating them all in a single column. 
    • You can now sort by method name by right-clicking on the method header or selecting the Sort By menu item from the right-click menu.
    • With the results tree list, the following new right-click menu options are available:
      • Expand All – expands all nodes
      • Collapse All – collapses all nodes
      • Sort by Most Recent First – puts the most recent result sets at the top of the list rather than at the bottom

     Note

    The results beneath a tree node are not filled until the node is expanded.  This is done to increase performance if you have a large result sets.

    GENDBC.PRG

    The Gendbc.prg program which generates program used to recreate a database has been updated with following minor enhancements:

    • Support for new Varchar, Varbinary and Blob field types
    • Support for AllowSimultaneousFetch, RuleExpression, and RuleText properties for views

    Environment Manager Task Pane

    The Environment Manager Task Pane has been enhanced with the following features:

    • Form and Formset Template Classes – you can now specify template classes for new forms and formsets with each environment set. This is setting specified in the Forms Tab, Options Dialog Box.
    • Field Mapping – you can set classes to use for when you drag and drop a field onto a form with each environment set. This is setting specified in the Field Mapping Tab, Options Dialog Box.
    • Resource File – the Environment Manager now supports setting of a Resource File. If one does not exist, the Environment Manager will optionally create it when the environment is set.
    • The Environment Manager now contains a new <default field mapping> environment set. This set is created the first time the Environment Manager is run so that the original default Options dialog settings for Field Mapping and Form Template Classes can be saved and restored later if desired.
    • For more information, see Environment Manager Task Pane.

    Data Explorer Task Pane

    The Task Pane Manager includes the new Data Explorer Task Pane which allows you to view and work with remote data sources such as SQL Server databases.

    For more information, see Data Explorer Task Pane.

    MemberData Editor

    The new MemberData Editor lets you edit MemberData for your classes. The MemberData Editor is available from the Class menu when the Class Designer is active. The MemberData Editor is also invoked silently when you right-click on an item in the Properties Window and select the Add to Favorites menu item. The MemberData Editor application is specified as a builder and can be changed in the Builder.dbf table located in your Wizards directory.

    For more information, see MemberData Editor and MemberData Extensibility.

    New Foundation Classes (FFC)

    The following are new FoxPro Foundation classes added to this version of Visual FoxPro:

    • _REPORTLISTENER.VCX – a set of core classes you can use when creating custom report listeners.
    • _FRXCURSOR.VCX – a class library used for working with report (FRX) files.
    • _GDIPLUS.VCX – a set of classes you can use for GDI+ handling. This is intended primarily for use when creating custom report listener classes.

    New Solution Samples

    Visual FoxPro 9.0 contains many new samples that show off new features in the product. To see a list of these samples, select the Solution Samples task pane in the Task Pane Manager and expand the New in Visual FoxPro 9.0 node.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Changes in Functionality for the Current Release

    Changes in Functionality for the Current Release

    • Article
    • 07/09/2007

    In this article

    1. Critical Changes
    2. Important Changes
    3. Miscellaneous Changes
    4. Removed Items
    5. See Also

    Visual FoxPro includes functionality that differs from previous versions and might affect existing code. These behavior changes are organized according to the following categories:

    • Critical Changes   Functionality changes most likely to affect existing code when running under this version of Visual FoxPro. It is extremely important that you read this section.
    • Important Changes   Functionality changes that might affect existing code when running under this version of Visual FoxPro.
    • Miscellaneous Changes   Functionality changes you should know about but are not likely to impact existing code.
    • Removed Items   Features or files that existed in prior versions of Visual FoxPro but are no longer included.

    Critical Changes

    Critical behavior changes will most likely to affect existing code when running under this version of Visual FoxPro.

    SQL SELECT IN (Value_Set) Clause

    In previous versions of Visual FoxPro, the IN (Value_Set) clause for the WHERE clause in the SQL SELECT command is mapped to INLIST( ) function. In the current release, Visual FoxPro might stop evaluating values and expressions in the Value_Set list when the first match is found. Therefore, if the IN clause is not Rushmore-optimized, you can improve performance by placing values most likely to match in the beginning of the Value_Set list. For more information, see the description for the IN clause in the SELECT – SQL Command topic and the INLIST( ) Function.

    Conversion of INLIST( ) Function in the Query Designer and View Designer

    In previous versions of Visual FoxPro, the Query Designer and View Designer convert INLIST( ) function calls in the WHERE clause of the SQL SELECT command into IN (Value_Set) clauses. In the current release, this conversion no longer occurs due to the differences between INLIST( ) and the SQL IN clause. INLIST( ) remains restricted to 24 arguments. For more information, see the description for the IN clause in the SELECT – SQL Command topic and the INLIST( ) Function.

    Grids and RecordSource and ControlSource Properties

    In Visual FoxPro 9.0 there is a change in Grid control behavior. When the RecordSource property for a Grid control is set, Visual FoxPro 9.0 resets all ControlSource properties to the empty string (“”) for all columns. In earlier versions of Visual FoxPro, the ControlSource properties were not properly reset, so problems could occur when a RecordSource with a different structure was later bound. This change may impact scenarios involving Access and Assign methods or BINDEVENT( ) function calls made against a Grid column’s ControlSource property.

    Important Changes

    Important changes might affect existing code when running under Visual FoxPro 9.0.

    Reporting

    Visual FoxPro contains many improvements for reporting. The following are behavior changes that could impact existing reports:

    • The Report Designer and Engine now make use of extensible components. You can control or eliminate use of design-time extensions by altering the value of _REPORTBUILDER System Variable. You control run-time extension use with the SET REPORTBEHAVIOR Command.
    • In Visual FoxPro 9’s new object-assisted reporting mode, report fields may need to be adjusted (widened) slightly. This is especially important for numeric data where a field that is not wide enough to display the entire number will show it instead as asterisks (*****). For more information about the changes to the Report System that required this change, and features of the GDI+ rendering engine and other changes related to it, see Using GDI+ in Reports. For migration strategy and recommendations, see Guide to Reporting Improvements.
    • For a table of additional, minor rendering differences between backward-compatible reporting mode and object-assisted reporting mode, see the table below.Expand tableRendering featureBehavior in backward-compatible modeBehavior in object-assisted modeRecommendationsTab stops (CHR(9) values included in report data)The width of a tab stop is determined by a number of characters in the font used.Tab stops are set at fixed-width positions, regardless of font.If you concatenated tabs with data in a stretching report layout element to create a table format within the element, you can often fulfill the same requirements using a second detail band in Visual FoxPro 9. Alternatively, change the number of tabs you concatenate with your data.Special characters and word-wrappingNon-breaking spaces are not respected; they are treated as normal space characters.Special characters such as non-breaking spaces (CHR(160)) and soft hyphens (CHR(173)) are correctly interpreted. As a result, words may wrap differently in output.Evaluate the results. In most cases, users will appreciate the change, because it more faithfully representing their original intentions in the text. If necessary, use the CHRTRAN( ) Function or STRTRAN( ) Function to replace these special characters with standard spaces and hyphens.Line spacing of multi-line objectsLine spacing is determined by a formula that does not take font properties into consideration. Lines in a multi-line object are individually rendered, so background colors for each line may appear to have a different width.GDI+ line spacing is dynamically determined using font characteristics. A multi-line object is rendered as a single block of text.Evaluate the results. In most cases, the change in line spaces will provide a more polished appearance, and in all cases this method of handling multi-line text provides better performance. If a report depends on the old style of spacing lines, you can adjust the ReportListener’s DynamicLineHeight Property to revert to the old behavior.Cursor images (.CUR files).CUR files can be used as image sources in reports..CUR files are not supported as image sources.Convert the cursor file to another, supported image format.Shape (Rounded Rectangle) curvatureLimited choices for curvature.More choices are available through the Report Builder Application dialog box interface, but some will not look the same way in backward-compatible mode and object-assisted mode.If reports have to run in both backward-compatible mode and object-assisted mode, or if they are designed in version 9.0 but must run in earlier versions, limit your choices of values of shape curvature to those allowed in the native Round Rectangle Dialog Box. If you are using the Style Tab, Report Control Properties Dialog Box (Report Builder) to design such reports, use the values 12, 16, 24, 32, and 99, to represent the native buttons, selecting the buttons from left to right. The default value in the Round Rectangle dialog box (second button) is 16.
    • When you create a Quick Report, by using the CREATE REPORT – Quick Report Command or by invoking the Quick Report… option on the Report menu, and if you have SET REPORTBEHAVIOR 90, the layout elements created by the Report Designer are sized differently from ones created for the same fields in previous versions. This change handles the additional width required by the new rendering mechanism of the report engine.
    • If you use the KEYBOARD Command or PLAY MACRO Command statements to address options on the Report menu, you may need to revise the keystrokes in these statements, as the menu has been reorganized.
    • Reports may take longer to open in the Report Designer if the report was previously saved with the Printer Environment setting enabled. You can improve performance by unchecking the Printer Environment menu item from the Report menu and re-saving the report. The saved Printer Environment is not critical for functioning of a report and is typically not recommended. Object-assisted report mode also respects different printers’ resolution settings, so saving resolution information for one printer in your report may have adverse effects in an environment with printers that have different resolutions. A saved Printer Environment may also have more adverse affects on REPORT FORM or LABEL commands invoked with the TO FILE option than they did in previous versions, if the associated printer setup is not available in the environment at runtime. In Visual FoxPro 9, the global default for this setting in the Reports Tab, Options Dialog Box, and for reports created in executable applications (.exe files), has been changed to unchecked.
    • Because of changes to the way Visual FoxPro 9 uses current printer settings to determine items such as print resolution and page height dynamically, a REPORT FORM or LABEL command will not run in object-assisted mode if there are no available printer setups in the environment or if the print spooler has been stopped. You will receive Error loading printer driver (Error 1958). If you need to run reports in an environment with no printer information, perhaps creating custom types of output that do not require printers, you can supply Visual FoxPro with the minimal set of information it needs to run your report by supplying a page height and page width from the appropriate Report Listener methods. For more information, see GetPageHeight Method and GetPageWidth Method.
    • By default, and by design, the Report Builder Application does not automatically show tables in the report’s Data Environment when you build report expressions. To better protect end-user design sessions, only tables you have explicitly opened, not all tables from the DataEnvironment, are available in the Expression Builder. With this change, you have the opportunity to set up the design session’s data exactly the way you want the end-user to see it, before you issue a MODIFY REPORT Command in your application. If you prefer the Report Designer’s old behavior, you can change the Report Builder Application to emulate it. For more information, see How to: Replace the Report Builder’s Expression Builder Dialog Box.
    • The ASCII keyword on the REPORT FORM Command is documented as following the <filename> parameter of the TO FILE<filename> clause. In earlier versions of Visual FoxPro, you could safely use the incorrect and unsupported syntax TO FILE ASCII<filename> instead. This incorrect syntax triggers an error in Visual FoxPro 9. Note that the ASCII keyword has no effect on object-assisted mode output provided by the Report Engine, although a ReportListener Object can be written to implement it.
    • The keyword NOCONSOLE has no default meaning in object-assisted reporting mode, because ReportListeners do not echo their rendering output to the current output window by default. However, a ReportListener can mimic backward-compatible mode in this respect, if desired. Refer to OutputPage Method for a complete example.
    • To facilitate development of run-time reporting extensions, the Report Engine now allows normal debugging procedures during a report run. If your error handling routine assumes it is impossible for a report to be suspended, this assumption will now be challenged. Refer to Handling Errors During Report Runs for a detailed look at the associated changes, and some suggestions for strategy.
    • REPORT FORM and LABEL commands are no longer automatically prohibited as user-interface-related commands in COM objects compiled into DLLs, when you run the commands in object-assisted mode. The restriction still applies to these commands when they are run in backward-compatible mode. (The topic Selecting Process Types explains why user-interface-related commands are prohibited in DLLs.) This change is not applicable to multi-threaded DLLs. A number of user-interface-related facilities also are not available in DLLs (whether single- or multi-threaded). For example, the TXTWIDTH( ) Function and TextWidth Method depend on a window handle to function, so they are not available in a DLL. The CREATE REPORT – Quick Report Command relies on the same facilities as TXTWIDTH(), and therefore is not available in a DLL. However, in many instances, creating custom output using a ReportListener does not require any user-interface activity, so a REPORT FORM or LABEL command can now be used productively in a DLL. Using the SYS(2335) – Unattended Server Mode function to trap for potential modal states, as well as the new SET TABLEPROMPT Command, is recommended. Refer to Server Design Considerations and Limitations for more information.
    • Changes have occurred to the handling of group headers and footers in multi-column reports, when the columns flow from left to right (label-style layout). The revised behavior is more useful and behaves consistently with the new detail header and footer bands as well. For a description of the change, see How to: Define Columns in Reports.
    • In previous versions, the NOWAIT keyword on the REPORT FORM and LABEL commands was not significant when the commands were issued in the Command window rather than in a program. In Visual FoxPro 9’s object-assisted mode, when previewing instructions are interpreted by the Report Preview Application, this keyword is significant no matter where you issue the command. The Report Preview Application uses the NOWAIT keyword, consistently, as an instruction to provide a modeless preview form. For more information about the Report Preview Application, see Extending Report Preview Functionality.
    • Visual FoxPro 8 introduced the NOPAGEEJECT keyword on the REPORT FORM and LABEL commands, but applied the keyword only to printed output. In Visual FoxPro 9, NOPAGEEJECT has significance for all output targets, including PREVIEW. This keyword provides chained or continued report runs for multiple REPORT FORM and LABEL commands. To facilitate this behavior in preview mode, and to allow you to apply customization instructions to multiple previews, the Report Output Application caches a single ReportListener object instance for preview output, causing a change in behavior for multiple modeless report commands (REPORT FORM … PREVIEW NOWAIT). In the past, you used multiple REPORT FORM… PREVIEW NOWAIT statements in a sequence, your commands resulted in multiple report preview windows. In Visual FoxPro 9, when SET REPORTBEHAVIOR 90, these commands will result in successive report previews being directed to a single report preview window. TipYou can easily invoke the old behavior by creating multiple ReportListener object references and associating one with each separate REPORT FORM or LABEL command, using the OBJECT keyword. For more information about using the OBJECT syntax, see REPORT FORM Command. For information about receiving multiple object references of the appropriate type from the Report Output Application, see Understanding the Report Output Application.
    • In the process of reviewing and overhauling the native Report Engine, a number of outstanding issues regarding band and layout element positioning were addressed. For example, a field element marked to stretch and sized to take up more than one text line’s height in the report layout might have inappropriately pushed its band’s exit events to the next page in Visual FoxPro 8. In Visual FoxPro 9, the band’s exit events occur on the same page. Additional revisions improve record-pointer-handling in footer bands, when bands stretch across pages. These changes are not specific to object-assisted output rendering. If you have relied on undocumented behavior providing exact band or layout control placement in a particular report, you should review that report’s behavior in Visual FoxPro 9.

    Rushmore Optimization

    When character values are indexed, all values are considered to be encoded using the table’s code page. In previous versions of Visual FoxPro, when the current Visual FoxPro code page differed from a table’s code page, any attempt to look for a character value within that table’s index resulted in an implicit translation of the value from the current Visual FoxPro code page into the table’s code page. This could cause SQL or other Rushmore optimizable commands to return or act upon incorrect records.

    In Visual FoxPro 9 and later, by default, the optimization engine no longer uses existing character indexes for tables created with a non-current code page. Instead, Visual FoxPro builds temporary indexes to ensure correct results. This can result in a loss of optimization of SQL or other commands which were optimized in earlier VFP versions. To prevent this, ensure that the current Visual FoxPro code page returned by CPCURRENT( ) Function matches the table’s code page returned by CPDBF( ) Function. This requires either changing the current Visual FoxPro code page, or changing the table’s code page. For information on specifying the current Visual FoxPro code page, see Understanding Code Pages in Visual FoxPro. For information on specifying the code page for a table, see How to: Specify the Code Page of a .dbf File. If you cannot change either the Visual FoxPro codepage or the table codepage to match, you can force optimization to work as it did in Visual FoxPro 8 and prior versions using the SET ENGINEBEHAVIOR Command with either 80 or 70 as a parameter.

    SQL SELECT Statements

    • SELECT – SQL Command containing DISTINCT and ORDER BY clauses in which the ORDER BY field is not in the SELECT field list will generate an error in Visual FoxPro 9.0 with SET ENGINEBEHAVIOR 90 (Error 1808: SQL: ORDER BY clause is invalid.). The following example shows this:CopySET ENGINEBEHAVIOR 90 CREATE CURSOR foo (f1 int, f2 int) SELECT DISTINCT f1 FROM foo ORDER BY f2 INTO CURSOR res
    • SELECT – SQL Command containing DISTINCT and HAVING clauses in which the HAVING field is not in the SELECT field list will now generate an error in Visual FoxPro 9.0 with SET ENGINEBEHAVIOR 90 (Error 1803: SQL: HAVING clause is invalid.). An error is reported because the HAVING field is not in projection and DISTINCT is used. The following example shows this:CopySET ENGINEBEHAVIOR 90 CREATE CURSOR foo (f1 int, f2 int) SELECT DISTINCT f1 FROM foo HAVING f2>1 INTO CURSOR res
    • The number of UNION statements that can be used in a SELECT – SQL Command is no longer limited to 9. Parentheses are not completely supported with UNION statements and unlike previous versions may generate an error. If two or more SELECT statements are enclosed in parenthesis, an error is generated during compile (Error 2196: Only a single SQL SELECT statement can be enclosed in parentheses.). This behavior is not tied to any SET ENGINEBEHAVIOR Command level. The following example shows this error:CopySELECT * FROM Table1 ;   UNION ;   (SELECT * FROM Table2 ;   UNION ;   SELECT * FROM Table3) The following example compiles without an error:CopySELECT * FROM Table1 ;   UNION ;   (SELECT * FROM Table2) ;   UNION ;   (SELECT * FROM Table3)

    For more information, see SET ENGINEBEHAVIOR Command.

    Disabling TABLEREVERT( ) Operations During TABLEUPDATE( ) Operations

    For CursorAdapters, Visual FoxPro does not permit TABLEREVERT( ) operations during operations.

    For more information, see TABLEREVERT( ) Function and TABLEUPDATE( ) Function.

    Index Key Truncation during Index Updates

    An error (Error 2199) is now generated when index key truncation is about to occur, typically during index creation or modification. This can happen with use of a key that contains an expression involving a Memo field, whose length in not fixed, such as in the following example:

    INDEX ON charfld1 + memofld1 TAG mytag

    Similar issues can also occur with the SQL engine (such as during a SQL SELECT command or View creation) where it might fail to build a temporary index to optimize a join evaluation if it is unable to accurately determine the maximum size of the key.

    For more information, see Error building key for index “name”. (Error 2199).

    Memo Field Corruption

    Visual FoxPro will now detect if a Memo field in a class library (.vcx) or form (.scx) is corrupt when you try to open up that file in the designer. If the file contains a corrupt Memo field, an Error 41 such as following will occur:

    Memo file <path>\myclass.VCT is missing or is invalid.

    Additionally, similar Memo errors may occur if you have a Visual FoxPro table open and try to access contents of a corrupt Memo. The following sample code shows how you can detect the Error 41 memo file corruption:Copy

    TRY
      USE myTable EXCLUSIVE NOUPDATE 
      SCAN
        SCATTER MEMO MEMVAR
      ENDSCAN
    CATCH TO loError
      IF loError.ErrorNo=41
        * handle error here
      ENDIF
    ENDTRY
    USE IN myTable
    
    

    While it is possible that loss of data may occur, the following sample code may assist in repairing some or the entire file:Copy

    ON ERROR *
    USE myclass.vcx
    COPY TO myclass_bkup.vcx&&backup
    COPY TO myclass2.vcx
    USE
    DELETE FILE myclass.vc*
    RENAME myclass2.vcx TO myclass.vcx
    RENAME myclass2.vct TO myclass.vct
    COMPILE CLASSLIB myclass.vcx
    ON ERROR
    
    

    Visual Form and Class Extended Property Support

    Visual FoxPro 9 allows you to create custom properties in your visual class (SCX or VCX file) whose values can contain carriage returns and/or be of length greater than 255 characters. If you specify a property with a value like this through the Properties Window (i.e., the Zoom dialog box), Visual FoxPro will store it in a format such that you will no longer be able to edit that class in older versions of Visual FoxPro.

    Class Definitions

    The ability to have a property assignment set to instantiated object is no longer supported in a class definition and will generate an error. The following example shows this.Copy

    LOCAL oCustom
    oCustom = CREATEOBJECT('cusTest')
    DEFINE CLASS cusTest AS CUSTOM
        oRef = CREATEOBJECT('myclass')
    ENDDEFINE
    DEFINE CLASS myclass AS CUSTOM
    ENDDEFINE
    
    

    You can instead assign a property to an instantiated object reference in the Init event of your class.

    Merge Modules for Redistributable Components

    Visual FoxPro includes merge modules (MSM files) for use in redistributing shared components with your runtime applications. Merge modules are used by applications that can create Windows Installer based setups. For example, Visual FoxPro ships with merge modules that contain the Visual FoxPro runtime libraries as well as some common components including a number of ActiveX controls.

    For Visual FoxPro 9, the VFP9RUNTIME.MSM merge module contains the runtime libraries that you will need for your custom redistributable application. The VFP9RUNTIME.MSM merge module also has dependencies on the merge modules containing the Microsoft VC 7.1 runtime library (MSVCR71.DLL) and the GDI+ graphics library (GDIPLUS.DLL). Because of these dependencies, if you select the VFP9RUNTIME.MSM merge module in a Windows Installer tool such as InstallShield, the other dependent merge modules will automatically be selected as well.

    Note   For Windows XP and higher operating systems, Visual FoxPro uses the GDI+ graphics library that is installed in your Windows System folder.

    For Visual FoxPro 9, the merge module containing the VC runtime library no longer installs to the Windows System directory. Instead, this file is installed to your application’s directory. This is done in compliance with recommended component versioning strategies for Windows operating systems. The GDI+ library is installed into the same directory as your Visual FoxPro runtime libraries and is only installed on operating systems later than Windows XP (XP already includes the GDI+ library in its Windows System directory).

    Tip   There may be circumstances where you will want to install the VC or GDI+ library to another location such as the Windows System directory. You can do this with your Windows Installer application (e.g., InstallShield) by first selecting the merge module before selecting the VFP9RUNTIME.MSM one. Once you have selected a merge module, you can change its installation path.

    There are new merge modules for MSXML3 and MSXML4 XML parser components. The MSXML 3.0 component consists of the following merge modules:

    • MSXML 3.0 (msxml3_wim32.msm)
    • Msxml3 Exception INF Merge Module (msxml3inf_wim32.msm)
    • WebData std library (wdstddll_wim32.msm)

    There are two MSXML 4.0 modules that should be included with any custom setup:

    • MSXML 4.0 (msxml4sxs32.msm)
    • MSXML 4.0 (msxml4sys32.msm)

    MTDLL Memory Allocation

    Visual FoxPro contains a new PROGCACHE configuration file setting which specifies the amount of memory Visual FoxPro allocates at startup for running programs (program cache). This setting also determines memory allocated per thread for Visual FoxPro MTDLL COM Servers. In prior versions, this setting was not configurable and memory was allocated as a fixed program cache of a little over 9MB (144 * 64K). The new PROGCACHE setting allows you to set the exact size of the program cache or specify that dynamic memory allocation be used.

    Since MTDLL COM Servers can use up a great amount of memory if many threads are created, it is important that memory be allocated more efficiently for these scenarios. In Visual FoxPro 9, the new default setting for MTDLL COM Servers is -2 (dynamic memory allocation). For more information, see Special Terms for Configuration Files.

    Miscellaneous Changes

    The following are miscellaneous changes that you should know about but are not likely to impact existing code.

    CursorAdapter Changes

    In the current version of Visual FoxPro, the following behavior changes apply to the CursorAdapter object:

    Grid SetFocus Supported for AllowCellSelection

    You can now call a Grid control’s SetFocus Method and have the Grid receive focus when the AllowCellSelection Property is set to False (.F.) and the grid contains no records.

    EXECSCRIPT Function

    The EXECSCRIPT( ) Function now allows you to pass parameters by reference.

    Additionally, Visual FoxPro 9.0 tightens syntax validation of calls made from concatenation of parameters. The following code, which worked in prior versions of Visual FoxPro, now properly causes an error because the CHR(13) character breaks the call into two lines whereas it is supposed to be part of the parameter for the EXECSCRIPT call.Copy

      cRecPauseScript = "EXECSCRIPT('" + ;
        "?123" + CHR(13) + ;
        "?456" + ;
        "')"
      _VFP.DoCmd(cRecPauseScript)
    
    

    To make a valid call that does not cause a syntax error, you can use the following code:Copy

      cRecPauseScript = "EXECSCRIPT('?123'+CHR(13)+ '?456')"
      _VFP.DoCmd(cRecPauseScript)
    
    

    Listbox Control Click Event

    In the current version of Visual FoxPro, the PageUp, PageDown, Home and End keyboard keys now cause a Listbox control’s Click event to fire. In previous versions, these keys did not trigger the Click event to fire, unlike the arrow keys.

    PEMSTATUS( ) Function Returns False for Hidden Native Properties

    In previous versions of Visual FoxPro, the PEMSTATUS( ) function returned True (.T.) for hidden native properties of Visual FoxPro base classes when specifying a value of 5 for nAttribute. In the current release, PEMSTATUS( ) returns False (.F.) for these hidden native properties. For more information, see PEMSTATUS( ) Function.

    Changes to Options Dialog Box

    • In the Options dialog box, the List display count option has been moved from the Editor tab to the View tab. For more information, see View Tab, Options Dialog Box.
    • In previous versions of Visual FoxPro, you could output all the settings in the Options Dialog Box (Visual FoxPro) to the Command Window by pressing the SHIFT key when choosing the OK button to close the dialog. In the current release, these settings are now sent to the Debug Output Window. The Debug Output window must be opened in order for the settings to be directed there.

    FOXRUN.PIF

    The FOXRUN.PIF file that is used by the RUN | ! Command is no longer installed in the Visual FoxPro root directory. If Visual FoxPro detects the presence of a FOXRUN.PIF file during a RUN command, it will use COMMAND.COM to execute the specified RUN command. This may not be the desired SHELL program to use for a particular operating system, especially newer ones like Windows XP in which CMD.EXE is preferable.

    The current behavior for a RUN command without the existence of a FOXRUN.PIF file is that the RUN command will use the SHELL program specified by the operating system COMSPEC environment variable. With Windows XP, you can view and edit this variable by right-clicking your computer desktop’s My Computer icon and selecting the Properties dialog box (Advanced tab).

    The FOXRUN.PIF file is still available in the Tools directory if you need it for a particular reason.

    For more information, see RUN | ! Command.

    SCATTER Command

    The SCATTER command no longer allows for ambiguous use of both MEMVAR and NAME clauses in the same command. You can only include one of these clauses. In prior versions, the following code would not generate an error:Copy

    USE HOME()+"SAMPLES\Data\customer.dbf"
    SCATTER MEMVAR NAME oCust
    
    

    For more information, see SCATTER Command.

    SET DOHISTORY

    The SET DOHISTORY command, which is included for backward compatibility, was updated to send output to the Debug Output Window instead of the Command Window as in prior versions.

    SCREEN ShowTips Property

    The default value for _SCREEN ShowTips Property has been changed from False (.F.) to True (.T.). This change was made because new Memo and Field Tips support is now dependent on this setting.

    AllowCellSelection Does Not Permit Deleting Grid Rows When Set to False

    When the AllowCellSelection Property is set to False (.F.) for a Grid control, you cannot select a row for deletion by clicking the deletion column. For more information, see AllowCellSelection Property.

    Northwind Database

    The sample Northwind database has been updated. Five of the stored procedures now include calls to the SETRESULTSET( ) Function so that the Visual FoxPro OLE DB Provider will return a cursor when they are executed.

    Foundation Classes

    The _ShellExecute class contained in the _Environ.vcx FFC class library has been updated to include an additional parameter in the ShellExecute method.

    Wizards and Builders

    The Wizard/Builder selection dialog box now properly hides deleted entries in the Wizard and Builder registration tables.

    Specifying Western Language Script Values for GETFONT( ) Function

    In versions prior to this release, passing 0 as the nFontCharSet value for GETFONT( ) opened the Font Picker dialog box and displayed the Script list as unavailable. You could not specify 0 (Western) as the language script value, and setting it to 1 (Default) sets nFontCharSet to the default font setting only, which is determined by the operating system.

    In this release, passing 0 to GETFONT( ) opens the Font Picker dialog box with the Script list available and Western selected. The return value for GETFONT( ) also includes the return value for nFontCharSet.

    Removed Items

    HTML Help SDK

    The HTML Help 1.3 SDK no longer ships with Visual FoxPro.

    See Also

    Reference

    Guide to Reporting Improvements
    Data and XML Feature Enhancements
    SQL Language Improvements
    Class Enhancements
    Language Enhancements
    Interactive Development Environment (IDE) Enhancements
    Enhancements to Visual FoxPro Designers
    Miscellaneous Enhancements

    Visual FoxPro New Reserved Words

    • Article
    • 07/09/2007

    In this article

    1. _
    2. A
    3. B
    4. C

    Show 24 more

    The following tables list new words added to the Visual FoxPro language which are now reserved:

    _

    Expand table

    _MEMBERDATA_MENUDESIGNER_REPORTBUILDER
    _REPORTOUTPUT_ REPORTPREVIEW_TOOLTIPTIMEOUT

    A

    Expand table

    ADJUSTOBJECTSIZEADOCODEPAGEAFTERBAND
    AFTERRECORDREFRESHAFTERREPORTALLOWMODALMESSAGES
    ANCHORASQLHANDLESAUTOCOMPLETE
    AUTOCOMPSOURCEAUTOCOMPTABLEAUTOHIDESCROLLBAR

    B

    Expand table

    BEFOREBANDBEFORERECORDREFRESHBEFOREREPORT
    BLOB

    C

    Expand table

    CANCELREPORTCASTCLEARRESULTSET
    CLEARSTATUSCOMMANDCLAUSESCONFLICTCHECKCMD
    CONFLICTCHECKTYPECURRENTDATASESSIONCURRENTPASS

    D

    Expand table

    DECLAREXMLPREFIXDELAYEDMEMOFETCHDISPLAYORIENTATION
    DOCKABLEDOMESSAGEDOSTATUS
    DYNAMICLINEHEIGHT

    E

    Expand table

    EVALUATECONTENTS

    F

    Expand table

    FETCHMEMOCMDLISTFETCHMEMODATASOURCEFETCHMEMODATASOURCETYPE
    FIRSTNESTEDTABLEFRXDATASESSIONFOXOBJECT

    G

    Expand table

    GDIPLUSGRAPHICSGETAUTOINCVALUEGETDOCKSTATE
    GETPAGEHEIGHTGETPAGEWIDTHGETRESULTSET

    H

    I

    Expand table

    ICASEINCLUDEPAGEINOUTPUTINSERTCMDREFRESHCMD
    INSERTCMDREFRESHFIELDLISTINSERTCMDREFRESHKEYFIELDLISTISMEMOFETCHED
    ISPENISTRANSACTABLE

    J

    K

    L

    Expand table

    LISTENERTYPELOADREPORT

    M

    Expand table

    MAKETRANSACTABLEMAPBINARYMAPVARCHAR

    N

    Expand table

    NESTNESTEDINTONEXTSIBLINGTABLE

    O

    Expand table

    ONPREVIEWCLOSEOPTIMIZEORDERDIRECTION
    OUTPUTPAGEOUTPUTPAGECOUNTOUTPUTTYPE

    P

    Expand table

    PAGENOPAGETOTALPICTUREMARGIN
    PICTURESPACINGPICTUREVALPOLYPOINTS
    PREVIEWCONTAINERPRINTJOBNAMEPROGCACHE

    Q

    Expand table

    QUIETMODE

    R

    Expand table

    RECORDREFRESHREFRESHALIASREFRESHCMD
    REFRESHCMDDATASOURCEREFRESHCMDDATASOURCETYPEREFRESHIGNOREFIELDLIST
    REFRESHTIMESTAMPRENDERREPORTBEHAVIOR
    REPORTLISTENERRESPECTNESTINGROTATION

    S

    Expand table

    SCCDESTROYSCCINITSELECTIONNAMESPACES
    SENDGDIPLUSIMAGESETRESULTSETSQLIDLEDISCONNECT
    SUPPORTSLISTENERTYPE

    T

    Expand table

    TABLEPROMPTTIMESTAMPFIELDLISTTWOPASSPROCESS

    U

    Expand table

    UNLOADREPORTUNNESTUPDATECMDREFRESHCMD
    UPDATECMDREFRESHFIELDLISTUPDATECMDREFRESHKEYFIELDLISTUPDATESTATUS
    USECODEPAGEUSECURSORSCHEMAUSETRANSACTIONS

    V

    Expand table

    VARBINARYVARCHARVARCHARMAPPING

    W

    X

    Expand table

    XMLNAMEISXPATH

    Y

    Z

    See Also

    Reference

    Reserved Words (Visual FoxPro)

    Getting Started with Visual FoxPro

    • Article
    • 07/09/2007

    Microsoft Visual FoxPro is the object-oriented relational database management system that makes it possible for you to create state-of-the-art enterprise database solutions. Visual FoxPro includes professional productivity tools, documentation, and sample code for quickly building, managing, and deploying solutions.

    In This Section

    Related Sections

    • What’s New in Visual FoxPro
      Describes the new features and enhancements included in this version of Visual FoxPro.
    • Using Visual FoxPro
      Gives an overview of Visual FoxPro features, describes concepts and productivity tools for developing, programming, and managing high-performance database applications and components, and provides walkthroughs that help get you started. With the robust tools and data-centric object-oriented language that Visual FoxPro offers, you can build modern, scalable, multi-tier applications that integrate client/server computing and the Internet.
    • Developing Visual FoxPro Applications
      Includes conceptual information about how to develop Visual FoxPro applications, instructions for creating databases and the user interface, and other tasks needed to create Visual FoxPro applications.
    • Programming in Visual FoxPro
      Describes how understanding object-oriented programming techniques and the event-driven model can maximize your programming productivity and enable you to access the full power of Visual FoxPro.
    • Reference (Visual FoxPro)
      Includes Visual FoxPro general, programming language, user interface, and error message reference topics.
    • Samples and Walkthroughs
      Contains Visual FoxPro samples and walkthroughs that you can use for experimenting with and learning Visual FoxPro features.

    Locating Readme Files (Visual FoxPro)

    • Article
    • 07/09/2007

    The Readme.htm file is stored at the root of the Microsoft Visual FoxPro CD-ROM. Use your Internet browser to open and view the files.

    To locate Readme files for additional products included in the Visual FoxPro package, see the root of each product CD-ROM.

    See Also

    Other Resources

    Getting Started with Visual FoxPro

    Installing Visual FoxPro

    • Article
    • 07/09/2007

    The following sections describe information about installing Visual FoxPro.

    In This Section

    Related Sections

    • Customizing the Visual FoxPro Environment
      Explains how you can optimize your computer system, configure Visual FoxPro and development environment settings, restore your desktop, and how people with disabilities can improve accessibility to Visual FoxPro and Microsoft Windows.
    • Upgrading from Earlier Versions
      Describes how Visual FoxPro protects your investment in applications built in previous versions of FoxPro.
    • Getting Started with Visual FoxPro
      Provides information about where to find the Readme file, installing and upgrading from previous versions, configuring Visual FoxPro, and customizing the development environment.

    Requirements for Installing Visual FoxPro

    • Article
    • 07/09/2007

    Visual FoxPro has the following minimum system requirements:

    • Computer: PC with a Pentium class processor.
    • Peripherals: Mouse or pointing device
    • Memory: 64 MB RAM (128 MB or higher recommended)
    • Hard disk space:
      • Visual FoxPro Prerequisites: 20 MB
      • Visual FoxPro Typical Install: 165 MB
      • Visual FoxPro Maximum Install: 165 MB
    • Video: 800 x 600 resolution, 256 colors (High color 16-bit recommended)
    • Operating system: Developing applications with Visual FoxPro is supported only on Microsoft Windows 2000 Service Pack 3 or later, Windows XP and Windows Server 2003. You can create and distribute run-time applications for Windows 98, Windows Me, Windows 2000 Service Pack 3 or later, Windows XP and Windows Server 2003. NoteInstallation on Windows NT 4.0 Terminal Server Edition is not supported.

    See Also

    Tasks

    How to: Install Visual FoxPro

    How to: Install Visual FoxPro

    • Article
    • 07/09/2007

    You can install this version of Visual FoxPro from a CD-ROM or a network to a local hard drive. You must install Visual FoxPro on a local drive, not a mapped drive. There is no other preparation required before installing Visual FoxPro. You must have administrator privileges to install Visual FoxPro. It is recommended that you run with power-user privileges to use all the provided tools effectively.

    You can safely install or uninstall using Visual FoxPro Setup. If you are upgrading Visual FoxPro, you must first uninstall the previous version of of the program. Though both versions of Visual FoxPro can exist on the same computer, you cannot install the current version of Visual FoxPro in the same directory as the previous version.

    If you plan to publish XML Web services using Visual FoxPro, you might want to set up Internet Information Services (IIS) on a Windows 2000, Windows XP or Windows Server 2003 computer. Refer to your operating system documentation for instructions on how to set up and configure IIS.

     Note

    Visual FoxPro setup no longer installs any Windows operating system Service Packs or versions of Internet Explorer. It is highly recommended that you install the latest versions of these components before installing Visual FoxPro. Additionally, Visual FoxPro 9.0 is supported only on Windows 2000 Service Pack 3 or later. For details about installing the latest Service Pack, visit the following Microsoft Web page at https://www.microsoft.com/windows2000/.

    Full installation includes all Visual FoxPro program files, online help, and samples files.

    To install Visual FoxPro

    1. Quit all open applications. NoteIf you use a virus protection program on your computer, override it or turn it off before running the Installation wizard. The Installation wizard might not run properly with virus protection turned on. After installation, be sure to restart your virus protection program.
    2. Insert the Visual FoxPro CD.The Visual FoxPro Setup start page appears automatically.
    3. Click Install Visual FoxPro to launch Visual FoxPro Setup.
    4. To determine if you need additional components, click Prerequisites to display any necessary components.
    5. Click Install Now! to install any new components. If Visual FoxPro Prerequisites needs to only update components, click Update Now!
    6. You might need to restart your computer. When finished, click Done.Visual FoxPro Setup reappears.
    7. To continue installation, click Visual FoxPro.
    8. After accepting the End User License Agreement and entering the Product Key and your name, click Continue. NoteVisual FoxPro cannot be installed on a mapped drive. You must install Visual FoxPro on a local drive. Do not attempt to use the Map Network Drive functionality in Setup.
    9. On the Options page, select the features you want to install and click Install Now! to continue.
    10. When finished, click Done to return to Visual FoxPro Setup. Click Exit to return to the Visual FoxPro Setup start page.

    If you uninstall Visual FoxPro while the previous version of Visual FoxPro exists on your computer, certain shared registry keys used by the previous version of Visual FoxPro are removed. You must reinstall these critical shared registry keys.

    If you run Visual FoxPro from the Start menu, Visual FoxPro Setup automatically reinstalls these keys. If you start Visual FoxPro using other means, such as running the application executable directly, the setup program does not start automatically. You should use Add/Remove Programs in the Control Panel and the following steps to reinstall the registry keys manually:

    To manually reinstall Visual FoxPro 9.0 registry keys

    1. From the Start menu, click Control Panel.
    2. Click Add/Remove Programs.
    3. Click Change/Remove for Microsoft Visual FoxPro 9.0.
    4. Click Visual FoxPro and Repair/Reinstall.

    See Also

    Tasks

    How to: Install Additional Applications
    How to: Reinstall Visual FoxPro
    Troubleshooting Installation

    Reference

    Requirements for Installing Visual FoxPro

    How to: Install Additional Applications

    • Article
    • 07/09/2007

    This release includes copies of additional software that you can install and use with Visual FoxPro. These include:

    • InstallShield Express Limited EditionProvides the capability to package and deploy the applications that you create using Visual FoxPro. Visual FoxPro includes the InstallShield Express 5.0 Visual FoxPro Limited Edition. NoteThe limited and full editions of InstallShield Express 5.0 are considered two versions of the same product and cannot coexist. If you install one version on a computer where another already exists, the original is uninstalled automatically. Because the limited edition contains fewer features than the full edition, you should keep the full edition on your computer.
    • Microsoft SOAP Toolkit 3.0 SamplesProvides samples for demonstrating how to consume and publish XML Web services. Visual FoxPro Prerequisites installs the core SOAP Toolkit 3.0 components needed to access and publish XML Web services in Visual FoxPro.
    • Microsoft SQL Server 2000 Desktop Engine (MSDE)Provides a personal version of SQL Server.

    To install InstallShield Express Limited Edition

    1. Insert the Visual FoxPro CD.The Visual FoxPro Setup start page opens automatically.
    2. Click Install InstallShield Express.
    3. Follow the instructions in the InstallShield Express installation wizard.

    You can also locate the Setup.exe file for InstallShield Express in the InstallShield folder on the Visual FoxPro CD.

     Note

    Visual FoxPro 9.0 installs its redistributable merge modules in the same location as Visual FoxPro 8.0.

    The version of InstallShield Express included with Visual FoxPro 9.0 automatically uses the Visual FoxPro 9.0 merge module location.

     Note

    Visual FoxPro 9.0 requires certain merge modules when creating a Visual FoxPro 9.0 redistributable custom application setup program using InstallShield Express.

    You need to include the following merge modules when creating your custom setup program:

    • Microsoft Visual FoxPro 9 Runtime Libraries
    • Microsoft Visual C Runtime Library 7.1
    • GDI Plus Redist
    • MSXML 4.0
    • MSXML 3.0 (needed only for CURSORTOXML functions)
    • Microsoft Visual FoxPro 9 Runtime Language Libraries (specific language library files that may be needed for international applications)
    • Reporting Applications (needed for Visual FoxPro 9.0 reporting engine)

     Note

    MSXML 4.0 consists of two merge modules (msxml4sxs32.msm and msxml4sys32.msm). MSXML 3.0 consists of three merge modules (msxml3_wim32.msm, msxml3inf_wim32.msm and wdstddll_wim32.msm).

    To install SOAP Toolkit 3.0 Samples

    1. Insert the Visual FoxPro CD.The Visual FoxPro Setup start page opens automatically.
    2. Click Install SOAP Toolkit 3.0 Samples.
    3. Follow the instructions in the SOAP Toolkit 3.0 Samples Setup Wizard.

    You can also locate the Soapsdk.msi and Soapsamp.msi files for the SOAP Toolkit in the SOAPToolkit folder on the Visual FoxPro CD.

    To install MSDE

    1. Insert the Visual FoxPro CD.The Visual FoxPro Setup start page opens automatically.
    2. Click Install Microsoft SQL Server Desktop Engine (MSDE) and follow the installation instructions that appear in the Readme file.

    You can locate the Setup.exe file for MSDE in the SQLMSDE folder on the Visual FoxPro CD.

     Note

    Visual FoxPro includes Microsoft SQL Server 2000 Desktop Engine Service Pack 3.0a. To make sure you have the most recent version and Service Pack installed, visit the Microsoft SQL Server Web page at https://www.microsoft.com/sql. In addition, if you are distributing custom Visual FoxPro applications that require MSDE, you can obtain the redistributable merge modules from the Microsoft SQL Server Web page for use with Windows Installer-based setup programs.

    See Also

    Tasks

    How to: Install Visual FoxPro

    How to: Reinstall Visual FoxPro

    • Article
    • 07/09/2007

    You can reinstall Visual FoxPro by uninstalling it and then installing it again. You can uninstall Visual FoxPro from the Start menu or from the original installation disk.

    To uninstall Visual FoxPro

    1. On the Start menu, click Control Panel.
    2. In the Control Panel window, double-click Add or Remove Programs.The Add or Remove Programs window opens.
    3. In the Currently installed programs list, click the version of Microsoft Visual FoxPro you want to uninstall, and then Change/Remove.

    If you reinstall Visual FoxPro or reinstall to another location, you might want to clean your user settings and other files installed by Visual FoxPro before reinstalling.

    You can remove these files by deleting the contents of the …\Application Data\Microsoft\Visual FoxPro folder inside your user settings folder. To determine the location of the Application Data folder, type ? HOME(7) in the Command window. These files include your FoxUser.* resource files, which contain user settings, and folders for the Toolbox and Task Pane.

    However, it is possible that your resource files are in another location. You can determine their location by typing the following in the Command window:Copy

    ? SYS(2005)
    
    

    You should delete old Code Reference files that might be associated with projects in the project directories. These are labeled as projectname_ref.* files. You might also need to restore the default Visual FoxPro registry settings.

    Visual FoxPro includes the VFPClean.app tool so you can make sure all core Xbase and other files are set appropriately.

    To run VFPClean.app

    • Type the following line of code in the Command window:CopyDO HOME()+"VFPCLEAN.APP"

    See Also

    Tasks

    How to: Install Visual FoxPro

    Troubleshooting Installation

    • Article
    • 07/09/2007

    You might encounter the following issues when installing Visual FoxPro:

    • If you cannot run Visual FoxPro and do not see error messages telling you what is wrong, the problem might be in your computer’s ROM BIOS or the video driver you are using.
    • If you are using an extended keyboard, be sure it is compatible with the ROM BIOS. In addition, make sure that you are using a standard VGA or Super VGA Windows video driver.
    • If you get a “stack overflow” error message, your video driver is out of date or not designed for your video card. To correct this problem, update the video driver.
    • For additional information, see the Visual FoxPro Readme at the root of the Visual FoxPro installation CD.

    Upgrading from Earlier Versions

    • Article
    • 07/09/2007

    In this article

    1. Conversion to Visual FoxPro Format
    2. See Also

    Microsoft Visual FoxPro protects your investment in applications built with previous versions of FoxPro. In Visual FoxPro, you can run many applications that were written in earlier versions with little or no conversion. You can modify and enhance applications using the Visual FoxPro language, knowing that most extensions to the language do not affect backward compatibility. In addition, you can convert FoxPro screens, projects, and reports to Visual FoxPro format.

    However, it is possible that some behavior or feature changes in the current version of Visual FoxPro might affect existing Visual FoxPro source code. Therefore, you should review the new features, enhancements, and most recent behavior changes for this version. For more information, see What’s New in Visual FoxPro and Changes in Functionality for the Current Release.

    Conversion to Visual FoxPro Format

    If you choose to convert your dBASE or FoxPro files to the Visual FoxPro format, you can take advantage of the unique features of Visual FoxPro. You can run many files from some previous versions of FoxPro directly; others require varying levels of conversion.

    You can convert most projects or components created using previous versions of Visual FoxPro simply by opening or recompiling them in this version of Visual FoxPro. When you recompile components, such as forms, screens, or reports, some modifications may be necessary. You can make modifications to these components in the same way you modify the components of this version of Visual FoxPro.

    You can find additional information about upgrading from previous versions of Visual FoxPro on the Microsoft Developer Network (MSDN) Web site at https://msdn.microsoft.com. You can search the MSDN Archive for documentation of previous versions of Visual FoxPro.

    See Also

    Other Resources

    Getting Started with Visual FoxPro
    Overview of Visual FoxPro Features
    Installing Visual FoxPro
    Customizing the Visual FoxPro Environment
    Optimizing Your System

    How to: Convert Earlier Visual FoxPro Files

    • Article
    • 07/09/2007

    You can explicitly convert FoxPro 2.6 and Visual FoxPro 3.0 files to the current Visual FoxPro format, which is necessary when you want to use these files with later versions of Visual FoxPro. Files that are created from later versions are converted automatically.

    To convert FoxPro 2.6 and Visual FoxPro 3.0 files

    1. On the File menu, click Open.
    2. In the Open dialog box, browse for and select the file.The Visual FoxPro Converter dialog box opens. For more information, see Visual FoxPro Converter Dialog Box.
    3. In the Visual FoxPro Converter dialog box, select the options you want.
    4. To complete the file conversion, click Continue. NoteIf you are converting Macintosh or MS-DOS files that have never contained Windows records, the Visual FoxPro Transporter dialog box appears. For more information, see Visual FoxPro Transporter Dialog Box.

    You can also convert FoxPro 2.6 and Visual FoxPro 3.0 files by typing one of the following commands with the file name in the Command window:

    See Also

    Concepts

    Upgrading from Earlier Versions

    Customizing the Visual FoxPro Environment

    • Article
    • 07/09/2007

    After you install Visual FoxPro, you might want to customize your development environment. You can also specify configuration settings that load when you start Visual FoxPro.

    For information on optimizing your Visual FoxPro applications, see Optimizing Applications.

    In This Section

    • Optimizing Your System
      Explains how to get maximum performance by optimizing your operating system, Visual FoxPro, and your application.
    • Visual FoxPro Configuration
      Explains how changing the configuration of your copy of Visual FoxPro affects the way it looks and behaves, such as establishing default locations for files used with Visual FoxPro, altering how your source code looks in an edit window, and displaying the format of dates and times.
    • Visual FoxPro Environment Settings
      Describes different ways to change Visual FoxPro environment settings such as using the Options dialog box, setting configuration options at program startup, and using command-line options. You can configure Visual FoxPro toolbars, dock windows, set editor options, and customize the appearance of your applications without altering code.
    • Restoring the Visual FoxPro Interactive Environment
      Describes how to close down all program operations and clear the Visual FoxPro desktop to return to its interactive state.
    • Accessibility for People with Disabilities (Visual FoxPro)
      Provides information about features, products, and services that make Microsoft Visual FoxPro and the Windows operating system more accessible for people with disabilities.

    Related Sections

    • Getting Started with Visual FoxPro
      Provides information about installing, upgrading, and customizing Visual FoxPro.
    • Using Visual FoxPro
      Explains how Visual FoxPro provides the tools you need to create and to manage high-performance database applications and components.
    • Samples and Walkthroughs
      Describes how to create different types of applications and components with step-by-step guides.
    • Overview of Visual FoxPro Features
      Describes how Visual FoxPro gives you more of everything you have come to expect in a database management system (DBMS) — speed, power, and flexibility.
    • Developing Visual FoxPro Applications
      Includes conceptual information about how to develop Visual FoxPro applications, instructions for creating databases and the user interface, and other tasks needed to create Visual FoxPro applications.
    • Programming in Visual FoxPro
      Describes how understanding object-oriented programming techniques and the event-driven model can maximize your programming productivity and enable you to access the full power of Visual FoxPro.
    • Development Productivity Tools
      Explains that Visual FoxPro provides developer tools for application development within the FoxPro application and the FoxPro language.

    Optimizing Your System

    • Article
    • 07/09/2007

    Visual FoxPro is designed to be a fast relational database development system. However, applications you create with Visual FoxPro can have varying requirements and purposes. Therefore, you might want to optimize the operating system, Visual FoxPro, or your application for maximum performance.

    In This Section

    Related Sections

    • Customizing the Visual FoxPro Environment
      Provides information about setting environment options, accessibility features, and configuration.
    • Getting Started with Visual FoxPro
      Discusses how to get started, including information about installing, upgrading, and customizing Visual FoxPro to create state-of-the-art enterprise database solutions.
    • What’s New in Visual FoxPro
      Lists the new features and enhancements made to this version of Microsoft Visual FoxPro.
    • Using Visual FoxPro
      Provides links to information on Visual FoxPro programming features that are designed to improve developer productivity, including Access and Assign methods, support for more graphic file formats, and language to simplify programming tasks.
    • Developing Visual FoxPro Applications
      Includes conceptual information about how to develop Visual FoxPro applications, instructions for creating databases and the user interface, and other tasks needed to create Visual FoxPro applications.
    • Programming in Visual FoxPro
      Discusses how to access the full power of Visual FoxPro by creating applications. Understanding object-oriented programming techniques and the event-driven model can maximize your programming productivity.

    Optimizing the Operating Environment

    • Article
    • 07/09/2007

    In this article

    1. Maximizing Memory and Virtual Memory
    2. Managing Your Hard Disk
    3. See Also

    You can optimize Visual FoxPro performance by maximizing your computer’s hardware and operating environment. The following sections describe how you can optimize these areas:

    • Maximizing Memory and Virtual Memory
    • Managing Your Hard Disk

    Maximizing Memory and Virtual Memory

    Providing your computer with as much memory as possible is the most effective way to optimize your system for Visual FoxPro. You can also use memory more effectively by closing all other running applications on your computer. To maximize the use of your computer’s memory while running Visual FoxPro, follow these guidelines:

    • Do not run other Windows applications while running Visual FoxPro.
    • Use only those memory-resident programs needed for operation.
    • Simplify the screen display.

    You can free memory by simplifying the way windows and screen backgrounds display on your computer monitor.

    • Use a color or a pattern for the desktop background instead of wallpaper.
    • Use the lowest-resolution display that is practical for you. The higher resolution of the display, the more memory your computer requires and the slower your graphics and user-interface elements appear. For VGA-compatible displays that use an extended mode driver, such as Video 7 or 8514, using the standard VGA driver ensures faster display performance but provides lower resolution and less color support.

    To increase the number of applications that you can run simultaneously, Microsoft Windows supports virtual memory by swapping segments of code that is the least recently used from memory to the hard disk in the form of a paging file. As a rule, the default settings in the Windows operating system for managing virtual memory meet the requirements of most users and are the recommended settings.

     Note

    The paging file does not improve Visual FoxPro performance and is not a substitute for more memory.

    Managing Your Hard Disk

    Managing your hard disk can improve overall product speed. To get the best performance from your hard disk, provide a generous amount of disk space. If your hard disk has little free space, you can increase Visual FoxPro performance by removing unnecessary data or by purchasing a hard disk with greater capacity.

    Disk input/output performance degrades significantly when a hard disk is nearly full. The more free hard disk space that is available, the more likely it is that contiguous blocks of disk space are available. Visual FoxPro uses this space for changes and additions to database, table, index, memo, and temporary files. Increasing free hard disk space improves performance of any commands that change or add to your files. More disk space also decreases the time required to read those files in response to your queries.

    The way that Windows and Visual FoxPro manage files on disk can greatly affect the performance of your application. The following sections discuss managing files in directories and temporary files:

    • Managing Files in Directories
    • Managing Temporary Files

    Managing Files in Directories

    As a directory becomes increasingly congested with files, the operating system takes longer to find files. The speed of your system when searching directories is a factor that Visual FoxPro does not control. To improve the speed of directory searches, reduce the number of files in your directories by performing the following actions:

    • Use the Visual FoxPro Project Manager to create and manage your files, segregate program files into separate directories, and avoid creating numerous generated files.
    • When you want to distribute your application, create an application or an executable (.exe) file instead of numerous individually generated files.This process decreases the number of files in your application’s subdirectories and increases performance.
    • If you delete a large number of files in one directory, copy the remaining files into a new directory or optimize the directory using a defragmenting utility program. NoteDeleting files from a directory does not automatically speed directory searching. When a file is deleted, the file is only marked for deletion and is still included in directory searches.
    • When saving files, use short file paths to increase performance.For example, suppose you have a file path “C:\Program Files\Microsoft Visual FoxPro\…”, which is a very long file path. Try to use shorter file paths.

    Managing Temporary Files

    Visual FoxPro creates temporary files for a variety of operations. For example, Visual FoxPro creates temporary files during editing, indexing, and sorting. Text editing sessions can also create a temporary or backup (.bak) copy of the edited file. By default, Visual FoxPro creates its temporary files in the same directory that Windows stores its temporary files unless you specifically designate an alternate location.

     Tip

    In most cases, you should specify one location for all Visual FoxPro temporary files. Make sure that the location you specify contains enough space for all possible temporary files.

    For more information, see How to: Specify the Location of Temporary Files.

    Searching for Temporary Files

    When Visual FoxPro searches for temporary files, for example, when you use the SYS(2023) – Temporary Path function to retrieve the temporary files path or when the TMPFILES, EDITWORK, PROGWORK, and SORTWORK settings in a Visual FoxPro configuration file do not specify a different location, the Windows API GetTempPath is used to search for the path containing the temporary files. GetTempPath searches a sequence of variables that differ depending on the operating system. Microsoft Windows 2000 and later include user variables that store the location of temporary files, while Microsoft Windows 95, 98, and Me include only global system environment variables for this purpose.

    On Windows 2000 and later, GetTempPath, and therefore, SYS(2023), TMPFILES, EDITWORK, PROGWORK, and SORTWORK, searches the TMP user variable for the location of temporary files by default. If the TMP user variable does not specify a location, Visual FoxPro searches the following variables in a specific order:

    • TMP system variable.
    • TEMP user variable.
    • TEMP system variable.

    If these variables do not specify a location, the location for storing temporary files defaults to the home drive and path, or the Temp folder in the user’s Documents and Settings directory.

     Note

    If more than one value is specified for TMP or TEMP, then the first value is used.

    On Windows 95, 98, and Me, GetTempPath searches the TMP and TEMP global system variables in that order and then searches the current directory.

    For more information, see SYS(2023) – Temporary Path and Special Terms for Configuration Files.

    See Also

    Concepts

    Optimizing Visual FoxPro in a Multiuser Environment
    Optimizing Visual FoxPro Startup Speed

    Optimizing Visual FoxPro Startup Speed

    • Article
    • 07/09/2007

    In this article

    1. Managing Startup Speed
    2. Optimizing the Load Size of Visual FoxPro
    3. Optimizing Key SET Commands
    4. See Also

    Though Visual FoxPro is always fast, you can optimize the startup and operating speed. This section describes enhancing Visual FoxPro performance by managing startup speed and optimizing SET commands.

    Managing Startup Speed

    The time required to load and start Visual FoxPro relates to the physical size of Visual FoxPro, the length of the PATH statement in effect, the number of items to be found at startup, and other factors. You can control the load size, the search path, component file locations, and the startup SET command values of Visual FoxPro.

    Managing File Locations

    Visual FoxPro stores the FoxUser.dbf file, which contains user settings, in the user’s Application Data directory by default. You can display this location by typing ? HOME(7) in the Command window. Visual FoxPro searches for the FoxUser.dbf and Config.fpw files in the following places:

    • In the startup application or executable file, if any.For example, you can start a Visual FoxPro application by typing the following code on the command line:Copy VFPversionNumber.exe MyApp.app  – or –CopyVFPversionNumber.exe MyApp.exe If the startup application or executable file contains a Config.fpw file, the configuration file is always used. You can override settings in a Config.fpw file that are bound inside an application by specifying an external Config.fpw file, using the -C command-line switch when starting an application or Visual FoxPro.
    • In the working directory.
    • Along the path established with the PATH environment variable.
    • In the directory containing Visual FoxPro.

    Controlling File Loading

    You can also speed startup by preventing Visual FoxPro from loading files you don’t plan to use. If your application does not use the FoxUser or FoxHelp file, disable them in the Config.fpw file by using the following commands:Copy

    RESOURCE = OFF
    HELP = OFF
    
    

    Visual FoxPro seeks all other Visual FoxPro components (GENXTAB, CONVERT, and so on) only in the Visual FoxPro directory. If you place components elsewhere, you must explicitly identify the path to those components in your Config.fpw file. For example, you might specify these locations:Copy

    _TRANSPORT = c:\migrate\transport.prg
    _GENXTAB = c:\crosstab\genxtab.prg
    _FOXREF = c:\coderefs\foxref.app
    
    

    You can use the environment variable FOXPROWCFG to explicitly specify the location of Config.fpw. For details about the FOXPROWCFG variable, see Customizing the Visual FoxPro Environment.

    Optimizing the Load Size of Visual FoxPro

    If you don’t plan on using any of the Visual FoxPro components listed previously, set them to an empty string to speed startup.

    To optimize the load size of Visual FoxPro, use the following syntax:Copy

            cFileVariable = ""
    
    

    Replace cFileVariable with _TRANSPORT, _CONVERT, or other variables as appropriate.

    Optimizing Key SET Commands

    You can optimize the operation of Visual FoxPro by tuning the values of certain SET commands.

    The following table shows SET commands that have the greatest effect on performance, and their settings for maximum performance. You can specify SET command values by including them in the Config.fpw file, by typing them in the Command window, or by setting them in the Options dialog box.

    Command Settings for Maximum PerformanceExpand table

    SET CommandPerformance Setting
    SET ESCAPE CommandON
    SET OPTIMIZE CommandON
    SET REFRESH Command0,0
    SET SYSMENU CommandDEFAULT
    SET TALK CommandOFF
    SET VIEW CommandOFF

    See Also

    Reference

    SET ESCAPE Command
    SET REFRESH Command
    SET SYSMENU Command
    Command Window (Visual FoxPro)

    Concepts

    Optimizing Visual FoxPro in a Multiuser Environment
    Optimizing the Operating Environment

    Other Resources

    Optimizing Your System
    Customizing the Visual FoxPro Environment

    Optimizing Visual FoxPro in a Multiuser Environment

    • Article
    • 07/09/2007

    In this article

    1. Managing Temporary Files
    2. Sharing Tables
    3. See Also

    When you run Visual FoxPro or Visual FoxPro applications in a multiuser environment, you can improve performance by managing storage of temporary files and controlling the way tables are shared.

    Managing Temporary Files

    In most multiuser environments, it is recommended that you save temporary files to local disks or memory when networked computers contain large amounts of free disk space. Redirecting storage of temporary files can improve performance by reducing frequent access to the network drive.

    On small networks with older networked computers and slow hard disks, you might experience better performance by leaving Visual FoxPro temporary files on the file server; however, when in doubt, direct temporary files to the local disk. When working on large, heavily used networks, always redirect temporary files to the local disk.

    By saving all temporary files to a single directory on a local hard drive, you can safely erase the contents of the temporary file directory on the file server prior to each Visual FoxPro session. This action purges the system of any temporary files that were created but not erased by Visual FoxPro due to a system reboot or power loss.

    For more information about temporary files, see Optimizing the Operating Environment and How to: Specify the Location of Temporary Files.

    Sharing Tables

    If users share tables on a network, the way you manage access to them can affect performance.

    • Avoid opening and closing tables repeatedly.
    • Buffer write operations to tables that are not shared.
    • Provide exclusive access to tables.
    • Limit the time on locking tables.

    Providing Exclusive Access

    You can enhance performance for the APPENDREPLACE, and DELETE commands and operations that run at times when no other users require access to the data, for example, overnight updates, by opening data files for exclusive use. When tables are open for exclusive use, performance improves because Visual FoxPro does not need to test the status of record or file locks.

    To open data files for exclusive use, use the EXCLUSIVE clause in the USE and OPEN DATABASE commands. For more information, see USE Command and OPEN DATABASE Command.

    Limiting the Time on Locking Tables

    You can reduce contention between users for write access to a table or record by shortening the amount of time for locking a record or table. Instead of locking a record while the user edits it, lock the record only after it has been edited. Using optimistic row buffering provides the shortest amount of time that records are locked. For more information, see Buffering Data.

    See Also

    Concepts

    Optimizing Applications in Multiuser Environments

    Visual FoxPro Configuration

    • Article
    • 07/09/2007

    The configuration of Visual FoxPro determines how your copy of Visual FoxPro looks and behaves. For example, you can establish the default locations for files used with Visual FoxPro, how your source code looks in an edit window, and the format of dates and times.

    You can make changes to the Visual FoxPro configuration that exist for the current session only (temporary), or specify them as the default settings for the next time you start Visual FoxPro (permanent). If the settings are temporary, they are stored in memory and are discarded when you quit Visual FoxPro.

    If you make permanent settings, they are stored in the Microsoft Windows registry or Visual FoxPro resource file. The Windows registry is a database that stores configuration information about the operating system, all Windows applications, OLE, and optional components such as ODBC. For example, the registry is where Windows stores the associations between file name extensions and applications so that when you click a file name, Windows can launch or activate the appropriate application.

    For an example of how to change the registry, you can examine Registry.prg in the \Samples\Classes directory, which contains numerous methods based on Windows API calls and makes it possible for you to manipulate the Windows registry.

    Similarly, Visual FoxPro stores its application-specific configuration information in the registry. When you start Visual FoxPro, the program reads the configuration information in the registry and sets the configuration according to those settings. After reading the registry, Visual FoxPro also checks for a configuration file, which is a text file in which you can store configuration settings to override the defaults stored in the registry. After Visual FoxPro has started, you can make additional configuration settings using the Options Dialog Box or SET commands. For more information, see How to: View and Change Environment Settings.

     Note

    The run-time version of Visual FoxPro does not read the Windows registry when starting up, as registry settings are designed primarily to configure the development environment. If you intend to distribute your Visual FoxPro applications using a run-time library, you can establish configuration settings in two ways: with a configuration file, or with a program that manipulates the Windows registry on the user’s computer.

    Visual FoxPro also maintains a resource file, Foxuser.dbf, that stores information about the current state of the program when you quit. For example, the resource file contains information about the location and size of the Command window, current keyboard macros, the toolbars that are displayed, and so on. The Foxuser.dbf file is an ordinary Visual FoxPro table, which you can read and change as required by your application.

     Tip

    If the data in the Foxuser.dbf file becomes corrupted or invalid, it can cause Visual FoxPro to behave in an erratic manner. If you do not manually store anything in the table, for example keyboard macros, deleting the table might help.

    See Also

    Tasks

    How to: View and Change Environment Settings
    How to: Change Configuration Settings in the Windows Registry

    Reference

    Options Dialog Box (Visual FoxPro)
    ODBC Registry Foundation Class
    Command Window (Visual FoxPro)
    SET RESOURCE Command

    Other Resources

    Customizing the Visual FoxPro Environment

    Visual FoxPro Environment Settings

    • Article
    • 07/09/2007

    You can make changes to the Visual FoxPro environment by using the Options dialog box, editing the Windows registry, overriding default configuration settings, customizing the Project Manager, and configuring Visual FoxPro toolbars.

    In This Section

    Related Sections

    How to: View and Change Environment Settings

    • Article
    • 07/09/2007

    In this article

    1. Displaying Environment Settings
    2. Saving Environment Settings
    3. Setting the Environment Using the SET Command
    4. See Also

    To view and change environment settings, use the Options dialog box. The Options dialog box contains a series of tabs representing different categories of environment options.

    To display the Options dialog box

    • On the Tools menu, click Options.

    The Options dialog box appears and displays tabs from which you can choose desired settings. For details about options you can set using each tab, see the Options Dialog Box (Visual FoxPro).

    Displaying Environment Settings

    When you run Visual FoxPro, you can verify environment settings by using the Options dialog box or the DISPLAY STATUS Command. Also, you can display the values of individual SET Commands to verify settings.

    To display multiple environment settings

    • On the Tools menu, click Options to display the Options dialog box and view the current settings. – OR – 
    • Type DISPLAY STATUS in the Command window.

    To display individual environment settings

    • Use the SET( ) Function in the Command window to display the current value of any SET commands.

    For example, to view the current status of SET TALK, type:Copy

    ? SET("TALK")
    
    

     Note

    Because settings are valid only for the current data session, you must capture your settings and place them in a program or a form’s Init event code for every private data session.

    For more information, see SET Command Overview.

    To echo Options dialog box settings to the Debug Output window

    1. On the Tools menu, click Debugger.
    2. Click the main Visual FoxPro window to select it and on the Tools menu, click Options.
    3. In the Options dialog box, make setting choices.
    4. Hold down the SHIFT key and click OK.The settings are echoed to the Debug Output window.
    5. Click the Visual FoxPro Debugger window to select it and copy the setting commands from the Debug Output window.

    Saving Environment Settings

    You can save the settings you make in the Options dialog box for the current data session or as default (permanent) settings for your copy of Visual FoxPro.

    To save settings for the current session only

    1. In the Options dialog box, select your settings.
    2. Click OK.

    When you save settings for the current session only, they remain in effect until you quit Visual FoxPro (or until you change them again). To save changes permanently, save them as default settings. This action stores your settings in the Windows registry.

    To save current settings as default settings

    1. In the Options dialog box, select your settings.
    2. Click Set As Default. NoteThe Set as Default button is disabled until you make a change to the current settings.

    You can override default settings either by issuing SET commands or by specifying a configuration file when you start Visual FoxPro. For details, see Setting Configuration Options at Startup.

    Setting the Environment Using the SET Command

    You can programmatically modify most options displayed on the tabs in the Options dialog box using SET commands or by assigning a value to a system variable.

     Note

    When you configure the environment using SET commands, the settings take effect only for the current session of Visual FoxPro. When you quit the program, the system discards your settings. This means you must reissue the SET commands. However, you can automate this process by issuing SET commands at startup or using a configuration file. For details, see Setting Configuration Options at Startup.

     Tip

    To save a configuration made with SET commands, open the Options dialog box and save your settings there.

    To set the environment programmatically

    • Use the SET commands that you want.

    For example, the following lines of code set a default path, add a clock to the status bar and use a year-month-date (yy.mm.dd) format for dates:Copy

    SET DEFAULT TO HOME()+"\VFP"
    SET CLOCK ON
    SET DATE TO ANSI      
    
    

    For more information, see SET Command Overview.

    See Also

    Tasks

    Get Application Information from the Windows Registry Sample
    How to: Change Configuration Settings in the Windows Registry

    Reference

    DISPLAY STATUS Command

    Other Resources

    Visual FoxPro Environment Settings

    How to: Specify the Location of Temporary Files

    • Article
    • 07/09/2007

    You can specify a different location for temporary files using the Visual FoxPro interface or by using the TMPFILES, EDITWORK, PROGWORK, and SORTWORK settings in a Visual FoxPro configuration file.

    To specify the location of temporary files

    1. On the Tools menu, click Options.
    2. In the Options dialog box, click the File Locations tab.
    3. In the File Type list, click Temporary Files, then Modify.
    4. In the Change File Location dialog box, type a new location or click the ellipsis (…) button to browse and select a location for temporary files.

    For more information, see Options Dialog Box (Visual FoxPro) and File Locations Tab, Options Dialog Box.

    See Also

    Reference

    Special Terms for Configuration Files

    Concepts

    Optimizing the Operating Environment
    Setting Configuration Options at Startup

    Other Resources

    Visual FoxPro Environment Settings

    How to: Change Configuration Settings in the Windows Registry

    • Article
    • 07/09/2007

    You can set the Visual FoxPro configuration by making changes directly in the Windows registry. To change the Windows registry, use the Registry Editor, a utility provided with Windows.

     Note

    Use caution when changing the Windows registry. Changing the wrong registry entry or making an incorrect entry for a setting can introduce an error that prevents Visual FoxPro, or even Windows itself, from starting or working properly.

    To change configuration settings in the registry

    1. In Windows, start the Registry Editor.
    2. In HKEY_CURRENT_USER node, browse to the Software\Microsoft\Visual FoxPro directory and open the folder for the current version of Visual FoxPro.
    3. In the Options folder, double-click the name of the setting to change, and then enter a new value.
    4. Close the Registry Editor.Your change will take effect the next time you start Visual FoxPro.

    You can also make changes to the registry by calling Windows APIs from a Visual FoxPro program.

    See Also

    Tasks

    Get Application Information from the Windows Registry Sample

    Reference

    SYS(3056) – Read Registry Settings

    Other Resources

    Visual FoxPro Environment Settings
    Accessing APIs

    Setting Configuration Options at Startup

    • Article
    • 07/09/2007

    In this article

    1. Using SET Commands in Applications
    2. Using a Configuration File
    3. See Also

    You can establish configuration settings when you first start the program, which allows you to override default settings.

    Using SET Commands in Applications

    One way to establish configuration settings is to issue one or more SET commands when your application starts. For example, to configure your system to display a clock in the status bar when the application starts, you can issue this SET command:Copy

    SET CLOCK ON
    
    

    The exact point at which you issue the SET command depends on your application. In general, you issue SET commands from your application’s main program file, which is the program or form that controls access to the rest of your application. You can also issue SET commands from the Load or Init events of the form. If you are using private data sessions, it may be necessary to make these settings in the BeforeOpenTables Event of your DataEnvironment object. For details about specifying a main file for an application, see Compiling an Application.

    If your application has a form set as main in the project manager, and it then launches a menu, you can add SETUP commands by entering them in the menu’s Setup option. For details see How to: Add Setup Code to a Menu System in Designing Menus and Toolbars.

     Tip

    An efficient way to manage SET commands for startup is to create a procedure that contains all the commands that you want to issue. You can then call the procedure from the appropriate point in your application. Keeping all the SETUP commands in a single procedure makes it easier to debug and maintain your configuration settings. You can also put the code in the class on which your application object is based, or the class on which your forms are based.

    Using a Configuration File

    In addition to setting the Visual FoxPro environment using the Options dialog box or SET commands, you can establish preferred settings and save them in one or more configuration files. A Visual FoxPro configuration file is a text file in which you can specify values for SET commands, set system variables, and execute commands or call functions. Visual FoxPro reads the configuration file when starting up, establishing the settings and executing the commands in the file. Settings made in the configuration file override default settings made in the Options dialog box (and stored in the Windows registry).

    Using a configuration file provides several advantages. You can:

    • Override the default settings established in the Options dialog box.
    • Maintain several different configuration files, each with different settings, so that Visual FoxPro can load a configuration suitable to a particular user or project.
    • Make changes more easily than if you establish settings with the SET commands in the program initialization sequence.
    • Start a program or call a function automatically when Visual FoxPro starts.

    For instructions about working with configuration files, see How to: Create a Configuration File and How to: Specify the Configuration File.

    See Also

    Tasks

    How to: Use Command-Line Options When Starting Visual FoxPro
    How to: Add Setup Code to a Menu System

    Reference

    _STARTUP System Variable

    Other Resources

    Compiling an Application
    Designing Menus and Toolbars
    Visual FoxPro Environment Settings

    How to: Specify the Configuration File

    • Article
    • 07/09/2007

    When Visual FoxPro starts, you can specify a configuration file or bypass all configuration files, allowing Visual FoxPro to use its default settings.

    When Visual FoxPro loads a configuration file, the settings in that file take precedence over corresponding default settings made in the Options dialog box.

    To specify a configuration file

    • In the command line that starts Visual FoxPro, specify the -C switch and the name of the configuration file that you want to use (including a path if necessary). Do not put a space between the switch and the file name.-or-
    • In Windows, double-click the name of the configuration file to use. Visual FoxPro will start using the configuration file you have selected.

    If you want to avoid using any configuration file, including the default file Config.fpw, you can suppress all configuration files. This causes Visual FoxPro to use only the default settings established in the Options dialog box.

    To suppress a configuration file

    • In the command line that starts Visual FoxPro, add the -C switch with nothing after it.For example, to avoid any configuration file found in the startup directory or the system path, use this command line:CopyVFPVersionNumber.exe -C

    Specifying an External Configuration File

    You can use an external configuration file in addition to an internal configuration file in circumstances where you need to configure settings separately. For example, setting SCREEN=OFF should be performed in an internal configuration file.

    You can set Visual FoxPro to read an external configuration file following an internal configuration file by using the new ALLOWEXTERNAL directive in the internal configuration file. When you include the setting ALLOWEXTERNAL=ON in the internal configuration file, Visual FoxPro searches for an external configuration file, usually Config.fpw, and reads its settings. You can also specify a different configuration file using the -C command-line switch when starting Visual FoxPro.

     Note

    For .exe and .dll file servers, Visual FoxPro supports only those configuration files bound inside the server. Therefore, Visual FoxPro disregards the ALLOWEXTERNAL setting.

    To read an external configuration file after an internal one

    1. In the internal configuration file, set the special term ALLOWEXTERNAL to on.CopyALLOWEXTERNAL = ON
    2. When you start your program, either specify a second configuration file using the -C command-line switch or have a second configuration file in the default program path.

    For more information about command-line switches, see How to: Use Command-Line Options When Starting Visual FoxPro.

    The settings in an external configuration file take precedence over those in the internal configuration file, if duplicate settings exist, because the external configuration file is read after the internal file. Visual FoxPro does not begin initialization until it reads both files.

    If you want to specify the configuration file as read-only, place the file in your project and mark it as Included. If you want to specify that the file can be modified, place the file in your project and mark it as Excluded. You can then distribute the file separately with your application or executable file. By convention, configuration files use the .fpw extension.

    See Also

    Tasks

    How to: Use Command-Line Options When Starting Visual FoxPro
    How to: Add Setup Code to a Menu System

    Reference

    _STARTUP System Variable

    Concepts

    Setting Configuration Options at Startup

    Other Resources

    Compiling an Application
    Designing Menus and Toolbars
    Visual FoxPro Environment Settings

    How to: Create a Configuration File

    • Article
    • 07/09/2007

    In this article

    1. Starting Applications or Programs Automatically
    2. See Also

    To create a configuration file, use the Visual FoxPro editor, or any editor that can create text files, to create a text file in the directory where Visual FoxPro is installed. Earlier versions of Visual FoxPro created the file Config.fpw in the startup directory. Config.fpw became the default configuration file. You can create any program file and use it to establish default settings and behaviors by starting Visual FoxPro using that file either by double clicking the file or using a command line reference.

    If you are creating a new configuration file, you can save it using any name you want. By convention, configuration files have the extension .fpw.

    When you start Visual FoxPro, you can use a default configuration file from the following locations in order:

    • Current working directory
    • Directory where Visual FoxPro is installed
    • Directories listed in the DOS path

    If Visual FoxPro does not find a configuration file in these locations, Visual FoxPro uses only the default settings established in the Options dialog box.

     Note

    For details about specifying an alternative to the default file name or location for the configuration file, see How to: Specify the Configuration File.

    Enter configuration settings using one of these methods:

    • Make settings with the SET command.
    • Set system variables.
    • Call programs or functions.
    • Include special terms used only in configuration files.

    To enter SET commands in a configuration file

    • Enter SET commands without the SET keyword and with an equal sign.For example, to set a default path type, use this format:CopyDEFAULT = HOME()+"\VFP" To add a clock to the status bar, use this command:CopyCLOCK = ON

    To enter a setting for a system variable, use the same syntax you would use in the Command window or in a program.

    To set system variables in a configuration file

    • Enter the name of the system variable, an equal sign (=), and the value to set the variable to.For example, the following command specifies an alternative spell-checking program:Copy_SPELLCHK = "SPLLCHK.EXE"

    You can also call functions or execute programs from within a configuration file by using the COMMAND command. For example, you can start an initialization program as part of the startup process.

    To call functions or execute commands in a configuration file

    • Enter COMMAND, an equal sign (=), and the command to execute or function to call.For example, to include the Visual FoxPro version number in the caption of the main Visual FoxPro window, use this command:CopyCOMMAND =_SCREEN.Caption="Visual FoxPro " + VERS(4) The following command launches a specific application when Visual FoxPro starts:CopyCOMMAND = DO MYAPP.APP

    You can also use special terms in a configuration file that do not correspond to SET value, system variables, or commands.

    To use special terms in a configuration file

    • Enter the special term, an equal sign (=), and the setting.For example, to set the maximum number of variables available in Visual FoxPro, use this command:CopyMVCOUNT = 2048

    For a complete list of special terms for configuration files, see Special Terms for Configuration Files.

    Starting Applications or Programs Automatically

    You can insert commands into a configuration file that automatically launches programs when Visual FoxPro starts. You can use these commands either to start an entire application or to start just a program, such as one that initializes system variables.

    To start applications from a configuration file

    1. Assign the name of your application to the _STARTUP System Variable anywhere in the configuration file:Copy_STARTUP = MYAPP.APP -or-
    2. Use the COMMAND command, which must be the last line in your configuration file:CopyCOMMAND = DO MYAPP.APP

    See Also

    Tasks

    How to: Use Command-Line Options When Starting Visual FoxPro
    How to: Add Setup Code to a Menu System

    Reference

    _STARTUP System Variable

    Concepts

    Setting Configuration Options at Startup

    Other Resources

    Compiling an Application
    Designing Menus and Toolbars
    Visual FoxPro Environment Settings

    Special Terms for Configuration Files

    • Article
    • 07/09/2007

    In this article

    1. Remarks
    2. See Also

    The following table lists special terms you can use in configuration files. You can also SET commands, system variables, and the _STARTUP setting in configuration files to customize the Visual FoxPro environment.

    Remarks

    Expand table

    Term and syntaxDescription
    ALLOWEXTERNAL ON | OFFSpecifies whether settings from an external configuration file as specified by the -C command-line switch (or located in path) are read in after those from an internal one. The ALLOWEXTERNAL term is ignored unless it is bound inside of an application.DefaultOFF
    BITMAP ON | OFFSpecifies whether Visual FoxPro first writes screen or form updates to an off-screen bitmap, and then performs a bit block transfer (bitblt) to the screen. BITMAP = OFF can improve performance when application are accessed using Windows Terminal Server clients.DefaultON
    CODEPAGE = nValue | AUTOSpecifies a number that identifies the character set used for files. Setting CODEPAGE to AUTO selects the current operating system code page.For the possible values you can use, see Code Pages Supported by Visual FoxPro.
    COMMAND = cVisualFoxProCommandSpecifies a Visual FoxPro command to execute when Visual FoxPro is started. The cVisualFoxProCommand specifies the command to execute.
    EDITWORK pathSpecifies where the text editor should place its work files. Because work files can become large, specify a location with plenty of free space.Default: Operating system dependent. For more information, see Optimizing the Operating Environment.
    INDEX extensionSpecifies the extension for Visual FoxPro index files.Default: .idx
    LABEL extensionSpecifies the extension for Visual FoxPro label definition files.Default: .lbx
    _MENUDESIGNER = cProgramNameSpecifies an external menu design application.Default: The empty string “”.
    MVCOUNTSets the maximum number of variables that Visual FoxPro can maintain. This value can range from 128 to 65,000.Default: 16,384
    OUTSHOW = ON | OFFDisables the ability to hide all windows in front of the current output by pressing SHIFT+CTRL+ALT.DefaultON
    PROGCACHE = nMemoryPagesSpecifies the amount of memory (address space) in pages that Visual FoxPro allocates at startup or a Visual FoxPro MTDLL COM Server allocates per thread for the internal program cache (memory used to run programs). Each page of memory is equal to 64K so the default setting equates to an allocation a little over 9MB. As the cache is filled, Visual FoxPro will try to flush it to remove unused items. It is possible that Visual FoxPro cannot free enough memory in which case an Error 1202 is generated (Program is too large). Adjusting the PROGCACHE setting can prevent this error from occurring. NoteWhile this setting can be used for the Visual FoxPro development product or normal runtime applications, it is primarily intended for MTDLL COM Servers where many threads are often created for a single server. In Visual FoxPro 9.0, the default value for MTDLL COM Servers is -2.When the value of nMemoryPages is greater than 0, Visual FoxPro allocates a fixed program cache. You can specify between 1 and 65000.If you specify 0 for nMemoryPages, no program cache is used. Instead, Visual FoxPro uses dynamic memory allocation based on determinations made by the operating system.If you pass a value for nMemoryPages that is less than 0, Visual FoxPro uses dynamic memory allocation but is limited to the specified memory (nMemoryPages * 64K). When the limit is reach, Visual FoxPro will flush allocated programs to free memory.You can call SYS(3065) to determine the current PROGCACHE setting. CLEAR PROGRAM will attempt to clear unreferenced code regardless of this setting. NoteThe Visual FoxPro OLE DB Provider ignores this setting since it uses dynamic memory allocation (PROGCACHE=0).Default144 (-2 for MTDLL)
    PROGWORK pathSpecifies where Visual FoxPro stores the program cache file.For faster performance, especially in a multiuser environment, specify a fast disk, such as a local disk or memory, if available. Provide at least 256K for the cache, though the file can grow larger.Default: Operating system dependent. For more information, see Optimizing the Operating Environment.
    REPORT extensionSpecifies the extension for Visual FoxPro report definition files.Default: .frx
    RESOURCE path[\file] | OFFSpecifies the location of the FoxUser.dbf resource file or prevents Visual FoxPro from using a resource file. The file argument is optional. If file is omitted, Visual FoxPro searches for the FoxUser.dbf file. If the specified file does not exist, it is created.Default: Startup directory as path and FoxUser.dbf as file.
    SCREEN = ON | OFFSpecifies whether the main Visual FoxPro window appears when opening Visual FoxPro.When an application consists of one or more top-level forms that are displayed in the Windows desktop, setting SCREEN to OFF can be useful, making the main Visual FoxPro window not required. For further information on top-level forms, see Controlling Form Behavior.DefaultON
    SORTWORK pathSpecifies where commands such as SORT and INDEX should place work files.Because work files can be up to twice as large as the tables being sorted, specify a location with plenty of free space. For faster performance, especially in a multiuser environment, specify a fast disk such as a local disk.Default: Operating system dependent. For more information, see Optimizing the Operating Environment.
    STACKSIZE = nValueSpecifies the number of nesting levels from 32 to 64,000 for operations such as the DO command. NoteYou can change the nesting level only during Visual FoxPro startup.Default: 128
    TEDIT [/N] editorSpecifies the name of the text editor used when you edit program files with MODIFY COMMAND or MODIFY FILE.Include the optional clause /N with TEDIT to specify a Windows text editor.Default: Visual FoxPro editor
    TITLE titleSpecifies the title that appears in the caption bar of the main Visual FoxPro window.Default: “Microsoft Visual FoxPro”
    TMPFILES pathSpecifies where temporary work files specified by EDITWORKSORTWORK, and PROGWORK configuration file settings are stored if they are not specified.Because work files can become very large, specify a location with plenty of free space. For faster performance, especially in a multiuser environment, specify a fast disk such as a local disk.Default: Operating system dependent. For more information, see Optimizing the Operating Environment.

    See Also

    Reference

    SET Command Overview

    Concepts

    Setting Configuration Options at Startup

    Other Resources

    Optimizing Applications
    Visual FoxPro Environment Settings

    How to: Use Command-Line Options When Starting Visual FoxPro

    • Article
    • 07/09/2007

    In addition to using the SET command and a configuration file, you can specify startup options by including a command-line switch. For example, using command-line options, you can suppress the display of the Visual FoxPro splash screen, which displays at the startup of Visual FoxPro, or specify a nondefault configuration file.

    To use a command-line switch

    • On the command line or in a shortcut, add the switch after the name of the Visual FoxPro executable file, VFPVersionNumber.exe where VersionNumber represents the version number of this release or any Visual FoxPro-created .exe file. NoteIf the command-line switch requires arguments, such as a file name, do not put a space between the switch and the argument. For example, to specify a configuration file, use a command such as:CopyC:\Program Files\Microsoft Visual FoxPro VersionNumber\VFPVersionNumber.exe -CC:\MYAPP.FPW Separate multiple options with single spaces.

    The following table lists the command-line switches available in Visual FoxPro.Expand table

    SwitchDescription
    -AIgnore the default configuration file and Windows registry settings.
    -BFileName,DurationDisplay a custom bitmap (.bmp), .gif, or .jpg graphic file and specify its display duration in milliseconds when Visual FoxPro starts. You can also include the -B command-line switch in a Visual FoxPro shortcut. NoteIf the bitmap you specify cannot be located, the bitmap does not display when Visual FoxPro starts.
    -CFileNameSpecify a configuration file, including a path if necessary, other than the default file, Config.fpw.
    -LFileNameSpecify a resource file, including a path if necessary, other than the default, vfp*ENU.dll, so you can use Visual FoxPro in a language other than the current language specified by Windows.
    -RIn earlier versions, refresh the Windows registry with information about Visual FoxPro, such as associations for Visual FoxPro files. In later versions, use /regserver.
    -TSuppress the display of the Visual FoxPro splash screen. By default, when Visual FoxPro starts, it displays a splash screen that shows the Visual FoxPro logo, version number, and other information. If you prefer that users of your application not see this splash screen, you can prevent Visual FoxPro from displaying it using the -T command-line switch.
    /?List the available command-line arguments. Available in Visual FoxPro 7.0 and later.
    /regserverRegister Visual FoxPro default registry keys.
    REGSVR32 server.dllRegister a .dll component.
    /unregserverRemove Visual FoxPro default registry keys.
    /u server.dllRemove a .dll component.

    See Also

    Tasks

    How to: Configure Visual FoxPro Toolbars

    Concepts

    Setting Configuration Options at Startup

    Other Resources

    Visual FoxPro Environment Settings
    Customizing the Visual FoxPro Environment

    How to: Configure Visual FoxPro Toolbars

    • Article
    • 07/09/2007

    In this article

    1. Activating and Deactivating Toolbars
    2. Customizing Existing Toolbars
    3. See Also

    Visual FoxPro includes the following customizable toolbars.Expand table

    ToolAssociated ToolbarsCommand
    Database DesignerDatabaseCREATE DATABASE
    Form DesignerForm ControlsForm DesignerColor PaletteLayoutCREATE FORM
    Print PreviewPrint Preview 
    Query DesignerQuery DesignerCREATE QUERY
    Report DesignerReport ControlsReport DesignerColor PaletteLayoutCREATE REPORT

    You can place as many toolbars on your screen as you need while working. You can dock toolbars to the top, bottom, or sides of your screen to customize your working environment. Visual FoxPro saves the positions of the toolbars so they remain where you last placed them.

    To dock a toolbar

    • Drag the toolbar to the top, bottom, or side of your screen.-OR-
    • Use the DOCK Command to dock the toolbar.

    Activating and Deactivating Toolbars

    By default, only the Standard toolbar is visible. When you use a Visual FoxPro designer tool (for example, the Form Designer), the designer displays the toolbars that you commonly need when working with that designer tool. However, you can activate a toolbar any time you require it.

    To activate a toolbar

    • Run the associated tool.–OR–

    To deactivate a toolbar

    • Close the associated tool.–OR–

    You can also programmatically activate and deactivate toolbars that have been previously activated by using the DEACTIVATE WINDOW or ACTIVATE WINDOW commands as in the following example.Copy

    IF WVISIBLE ("Color Palette")
    DEACTIVATE WINDOW("Color Palette")
    ENDIF
    
    

    Customizing Existing Toolbars

    The easiest way to create custom toolbars is by modifying the toolbars already provided with Visual FoxPro. You can:

    • Modify an existing toolbar by adding or removing buttons.
    • Create a new toolbar that contains buttons from existing toolbars.

    You can also define custom toolbars by creating a custom toolbar class using code. For details, see Designing Menus and Toolbars.

    You can modify any of the toolbars provided with Visual FoxPro. For example, you might want to remove a button from an existing toolbar, or copy buttons from one toolbar to another.

    To modify an existing Visual FoxPro toolbar

    1. From the View menu, choose Toolbars.
    2. Select the toolbar you want to customize and choose Customize.
    3. Remove buttons from the toolbar by dragging them off of the toolbar.
    4. Add buttons to the toolbar by selecting an appropriate category in the Customize Toolbar dialog box and then dragging the appropriate buttons onto the toolbar.
    5. Complete the toolbar by choosing Close in the Customize Toolbar dialog box and then closing the toolbar window. TipIf you change a Visual FoxPro toolbar, you can restore it to its original configuration of buttons by selecting the toolbar in the Toolbar dialog box and then choosing Reset.

    You can create your own toolbars comprised of buttons from other toolbars.

    To create your own toolbar

    1. From the View menu, choose Toolbars.
    2. Choose New.
    3. In the New Toolbar dialog box, name the toolbar.
    4. Add buttons to the toolbar by selecting a category in the Customize Toolbar dialog box and then dragging the appropriate buttons onto the toolbar.
    5. You can rearrange buttons on the toolbar by dragging them to the desired position.
    6. Complete the toolbar by choosing Close in the Customize Toolbar dialog box and then closing the toolbar window. NoteYou cannot reset buttons on a toolbar you create.

    To delete a toolbar you created

    1. From the View menu, choose Toolbars.
    2. Select the toolbar you want to delete.
    3. Choose Delete.
    4. Choose OK to confirm the deletion. NoteYou cannot delete toolbars provided by Visual FoxPro.

    See Also

    Tasks

    How to: Use Command-Line Options When Starting Visual FoxPro
    How to: Set Editor Options

    Other Resources

    Visual FoxPro Environment Settings
    Customizing the Visual FoxPro Environment
    Designing Menus and Toolbars

    How to: Dock Windows

    • Article
    • 07/09/2007

    In this article

    1. Docking Modes
    2. See Also

    You can dock certain Visual FoxPro Integrated Development Environment (IDE) windows to the Visual FoxPro desktop window, each other, or user-defined forms. The following windows are dockable:

    • Call Stack
    • Command
    • Data Session (View)
    • Debugger
    • Document View
    • Locals
    • Output
    • Properties
    • Trace
    • Watch

     Note

    Historically in prior versions of Visual FoxPro, the Data Session window has always been referred to as the View window. Additionally, language used to control this window, such as HIDE WINDOWACTIVATE WINDOWWONTOP( ), also refers to this window as the View window.

    When you drag a dockable window to a Visual FoxPro desktop window boundary, the dockable window repositions itself against the chosen boundary. The state and location of docked windows persist from the last user session.

    To dock windows programmatically and retrieve their dock states, see DOCK Command and ADOCKSTATE( ) Function.

    You can change the dockable status of a dockable window.

    To enable or disable dockable status

    1. Right-click the title bar of an open and dockable window.
    2. Click Dockable to enable or disable the dockable status of the window. A check mark appears when the window is dockable.-OR-
    3. Click the desired window to make the window active.
    4. On the Window menu, click Dockable to enable or disable the dockable status of the window. A check mark appears when the window is dockable.

    For more information about retrieving the dockable status for a window programmatically, see WDOCKABLE( ) Function.

    Docking Modes

    You can dock windows in three different modes:

    • Normal dockingWindows dock to a boundary of the Visual FoxPro desktop window.
    • Linked dockingWindows dock to each other and share a dockable window container.
    • Tabbed dockingWindows dock to each other and share the full window through the use of tabs.

    You can use tabbed docking and linked docking together.

    To create normal docking

    1. Make sure the docking status of the window or windows is set to Dockable.
    2. Drag the window title bar to a boundary of the Visual FoxPro desktop window.

    To create linked docking

    1. Make sure the docking status of the window or windows is set to Dockable.
    2. Drag the title bar of the desired window to a boundary or docking zone of the target window.

    The docking zone is indicated when the window you are dragging changes shape to fit the target window. Visual FoxPro creates an additional title bar for link-docked windows.

    To create tabbed docking

    1. Make sure the docking status of the window or windows is set to Dockable.
    2. Drag the title bar of the desired window to the title bar of the target window.

    Visual FoxPro adds tabs to the bottom boundary of the docked windows.

    To undock windows

    • To undock normal-docked windows, drag the title bar of the desired window away from the shared window boundary.-or-
    • To undock link-docked windows, drag the title bar of the desired window away from the shared window.-or-
    • To undock tab-docked windows, drag the tab of the desired window away from the shared window.

    You can disable docking behavior by holding the CTRL key while dragging a window.

    Deleting or editing the FoxUser.dbf resource file, which contains your settings, restores or changes your default window settings. For more information about altering your settings, see FoxUser Resource File Structure.

    See Also

    Tasks

    How to: Dock Forms
    How to: Dock Toolbars

    Reference

    Visual FoxPro System Windows
    Document View Window

    Other Resources

    Visual FoxPro Environment Settings

    How to: Set Editor Options

    • Article
    • 07/09/2007

    You can configure the window of the Visual FoxPro editor to display text the way you want such as setting the font, text alignment, or syntax coloring. You can also make the editor easier to use by setting preferences for indentation, wordwrap, automatic backup copies, and other features.

    To configure the editing window, set your preferences in the Edit Properties dialog box that appears after you open a program or text file open and select Properties from the Edit menu. For details about settings you can configure, see Edit Properties Dialog Box.

    To display the Edit Properties dialog box

    1. Open the editing window for a program, text file, or control.
    2. From the Edit menu that becomes active, select Properties. TipYou can display the Font dialog box directly by right-clicking the editing window and choosing Font from the shortcut menu.

    For more information about opening a program or text file, see How to: Create Programs.

    By default, the settings that you make in the Edit Properties dialog box persist for that file. For example, if you change the font, the font for all text in the current window changes. If you open another editing window, the default settings apply.

    You can choose to save your settings so that they apply to all files of the same type, or not to save the new settings at all. If you apply your settings to similar file types, Visual FoxPro uses the settings you make when you edit files with the same extension, for example, all .prg files, or all method code in the Form Designer.

    To avoid persisting changes to editor settings

    • In the Edit Properties window, clear the Save Preferences option and then click OK.

    To apply editor options to similar files

    • In the Edit Properties dialog box, select Use These Preferences As Default and then click OK.

    You can also set the color and font that the editor uses to identify keywords, comments, and other elements of programs. For details, see Editor Tab, Options Dialog Box.

    See Also

    Tasks

    How to: Change Configuration Settings in the Windows Registry
    How to: Display and Print Source Code in Color

    Reference

    Editing Window
    Editor Tab, Options Dialog Box

    Concepts

    Restoring the Visual FoxPro Interactive Environment

    Other Resources

    Visual FoxPro Environment Settings

    How to: Display and Print Source Code in Color

    • Article
    • 07/09/2007

    In this article

    1. Displaying Source Code in Color
    2. Printing Source Code in Color
    3. See Also

    You can display and print code with color syntax in the Command window and Visual FoxPro editors for program (.prg) files, methods, stored procedures, and memos.

    The following sections contain more information about displaying and printing code in color:

    • Displaying Source Code in Color
    • Printing Source Code in Color

    Displaying Source Code in Color

    You can turn on syntax coloring separately for each editing window; however; the color syntax settings that you choose in the Editor tab of the Options dialog box apply to the Command window and most other editing windows. For more information, see Editor Tab, Options Dialog Box and Editing Window.

     Note

    To display color syntax in run-time applications, Visual FoxPro must be configured to display color syntax. Run-time applications display only the default color settings because run-time applications do not check the Windows registry for settings that you change in the Options dialog box, which specifies the default settings for color syntax.

    When color syntax is turned on, Visual FoxPro performs background compilation for the current and single line of code that you are typing. When a line of code contains invalid syntax, Visual FoxPro displays the line of code with the selected formatting style.

    To display color syntax in an editing window

    1. Open the editing window that you want to display color syntax.
    2. On the Edit menu, choose Properties.
    3. In the Edit Properties dialog box, select the Syntax coloring check box.
    4. Click OK.

    Syntax coloring is activated for the editing window you selected. For more information, see Edit Properties Dialog Box.

    You can also display source code files to the screen in color using the TYPE command. For more information, see TYPE Command.

    To customize color syntax settings

    1. On the Tools menu, choose Options.
    2. In the Options dialog box, choose the Editor tab.
    3. Under Syntax color settings, choose the color settings that you want.
    4. When you are finished choosing settings, click OK.

    The color settings you chose take effect.

    To set formatting for invalid color syntax

    1. Turn on syntax coloring for the editing window.
    2. On the Tools menu, choose Options.
    3. In the Options dialog box, choose the Editor tab.
    4. In the Background Compile box, choose the formatting style you want.

    Invalid syntax displays in the editing window with the formatting style you chose.

    Printing Source Code in Color

    You can print source code in color wherever color syntax appears, such as program files, methods, stored procedures, and memos.

     Note

    To print your code files in color, you must be connected to a color printer to select the color printing options available for your printer. Background colors set in the Editor tab of the Options dialog box are not printed. If you select color printing for source code files, hyperlinks appear underlined.

    To print source code in color

    1. Open the program file or code you want to print.
    2. On the File menu, choose Print.
    3. In the Print dialog box, choose your color printer, and then click Preferences.
    4. Select the color printing options available for your printer.
    5. When you are finished, click Print.

    When connected to a color printer, you can also print source code in color using the TYPE command with the TO PRINTER clause.

    For more information, see Print Dialog Box (Visual FoxPro) and TYPE Command.

    See Also

    Tasks

    How to: Set Editor Options
    How to: View and Change Environment Settings

    Other Resources

    Visual FoxPro Environment Settings

    Automating Keystroke Tasks with Macros

    • Article
    • 07/09/2007

    If you find yourself repeating the same keystrokes, or if you want to automate some keystroke tasks for your users, you can record and save these keystrokes in macros by using the Macros dialog box.

     Note

    To use macros, any keyboard you use must have function keys F1 to F9, and a CTRL key or an ALT key.

    In This Section

    Related Sections

    • Visual FoxPro Environment Settings
      Describes different ways to change Visual FoxPro environment settings such as using the Options dialog box, setting configuration options at program startup, using command-line options. You can configure Visual FoxPro toolbars, dock windows, set editor options, and customize the appearance of your applications without altering code.
    • Optimizing Your System
      Provides information about optimizing your operating environment.
    • Development Productivity Tools
      Discusses the different tools available to help make creating Visual FoxPro applications easier and faster.

    How to: Clear Macro Definitions

    • Article
    • 07/09/2007

    You can clear macro key definitions through the Macros Dialog Box.

    To clear a macro definition

    1. From the Macros dialog box, select a macro from the Individual Macro list.
    2. Choose Clear.

    See Also

    Reference

    Macros Dialog Box

    Other Resources

    Automating Keystroke Tasks with Macros

    How to: Create, Save, and Restore Macro Sets

    • Article
    • 07/09/2007

    A macro set is a defined set of keys and their associated macros stored in a file with the extension .FKY. You can create macro sets through the Macros Dialog Box.

    To create a macro set

    1. From the Macros dialog box, create individual key macros.
    2. Choose Save.
    3. In the Save Current Macros To box, enter a name for the macro set and choose Save.

    To restore a macro set

    1. From the Macros dialog box, choose Restore.
    2. Select a macro set file and then choose OK.

    See Also

    Reference

    CLEAR Commands
    Macros Dialog Box
    RESTORE MACROS Command
    SAVE MACROS Command

    Other Resources

    Automating Keystroke Tasks with Macros

    How to: Edit Macros (Visual FoxPro)

    • Article
    • 07/09/2007

    You can edit existing macros through the Macros Dialog Box.

    To edit a macro

    1. From the Macros dialog box, select a macro from the Individual Macro list and then choose Edit.
    2. Modify the macro contents.
    3. Choose OK.

    See Also

    Reference

    CLEAR Commands
    Macros Dialog Box
    SET FUNCTION Command

    Other Resources

    Automating Keystroke Tasks with Macros

    How to: Record Macros (Visual FoxPro)

    • Article
    • 07/09/2007

    You can record keystroke macros in Visual FoxPro through the Macros Dialog Box.

    To record a macro

    1. From the Tools menu, choose Macros.
    2. In the Macros dialog box, choose Record.
    3. Press the key or type the key combination you want to define.
    4. Enter a name for the macro or accept the default and then choose OK.

    Visual FoxPro begins recording every keystroke.

     Note

    Macro names cannot contain spaces.

    To stop recording a macro

    1. From the Tools menu, choose Macros.
    2. In the Stop Recording Macro Dialog Box, choose from the following:
      • To save the macro as is, choose OK.
      • To continue recording, choose Continue.
      • To discard the macro, choose Discard.
      • To insert a literal keystroke (the literal meaning of a key instead of any meaning currently assigned to it), choose Insert Literal.
      • To insert a pause, select Seconds, add the amount of time, and choose Insert Pause.

    See Also

    Reference

    Macros Dialog Box
    ON KEY LABEL Command
    SET FUNCTION Command
    Stop Recording Macro Dialog Box

    Other Resources

    Automating Keystroke Tasks with Macros

    How to: Set a Default Macro Set

    • Article
    • 07/09/2007

    A default macro set exists when Visual FoxPro starts. You can create and set your own default macro set through the Macros Dialog Box.

    To set a default macro set

    1. From the Macros dialog box, create or restore a macro set.
    2. Choose Set Default.

    See Also

    Reference

    Macros Dialog Box
    RESTORE MACROS Command

    Other Resources

    Automating Keystroke Tasks with Macros

    In the Visual FoxPro Documentation
    What’s New in Visual FoxPro
    Describes the new features and enhancements included in this version of Visual FoxPro.

    Getting Started with Visual FoxPro
    Provides information about where to find the Readme file, installing and upgrading from previous versions, configuring Visual FoxPro, and customizing the development environment.

    Using Visual FoxPro
    Provides an overview of Visual FoxPro features, describes concepts and productivity tools for developing, programming, and managing high-performance database applications and components.

    Samples and Walkthroughs
    Contains Visual FoxPro code samples and step-by-step walkthroughs that you can use for experimenting with and learning Visual FoxPro features.

    Reference
    Includes Visual FoxPro general, programming language, user interface, and error message reference topics.

    Product Support
    Provides information about Microsoft product support services for Visual FoxPro.

    Additional Information

    Microsoft Visual FoxPro Web Site

    Provides a link to the Microsoft Visual FoxPro Web site for additional information and resources for Visual FoxPro.Microsoft Visual FoxPro Community

    Provides a link to Microsoft Visual FoxPro Online Community Web site for third-party community resources and newsgroups.Microsoft Visual FoxPro Training and Resources

    Provides a link to the Visual FoxPro training Web site to find information about training, books, and events for Visual FoxPro. Accessibility for People with Disabilities

    Provides information about features that make Visual FoxPro more accessible for people with disabilities.

    This release of Visual FoxPro contains many new features and enhancements. The following sections describe these new features and enhancements.

    In This Section

    Guide to Reporting Improvements

    A roadmap to all new Reporting enhancements.Data and XML Feature Enhancements

    Describes additions and improvements to Visual FoxPro data features.SQL Language Improvements

    Describes enhancements to SQL language such as SELECT – SQL Command.Class Enhancements

    Describes additions and improvements to Visual FoxPro classes, forms, controls and object-oriented related features.Language Enhancements

    Describes additions and improvements to the Visual FoxPro programming language.Interactive Development Environment (IDE) Enhancements

    Describes additions and improvements made to the Visual FoxPro IDE.Enhancements to Visual FoxPro Designers

    Describes improvements made to designers available in Visual FoxPro.Miscellaneous Enhancements

    Describes other improvements made in this version of Visual FoxPro.Changes in Functionality for the Current Release

    Describes changes in the behavior of existing language and functionality.Visual FoxPro New Reserved Words

    Lists new reserved words added to Visual FoxPro.

    Related Sections

    Getting Started with Visual FoxPro

    Provides information about where to find the ReadMe file and how to install and upgrade from previous versions, configure Visual FoxPro, and customize the development environment.Using Visual FoxPro

    Provides an overview of Visual FoxPro features, describes concepts and productivity tools for developing, programming, and managing high-performance database applications and components, and provides walkthroughs that help get you started. With the robust tools and data-centric object-oriented language that Visual FoxPro offers, you can build modern, scalable, multi-tier applications that integrate client/server computing and the Internet.Samples and Walkthroughs

    Contains Visual FoxPro code samples and step-by-step walkthroughs that you can use for experimenting with and learning Visual FoxPro features.Reference (Visual FoxPro)

    Describes Visual FoxPro general, programming language, user interface, and error message reference topics.Product Support (Visual FoxPro)

    Provides information about Microsoft product support services for Visual FoxPro.

    Guide to Reporting Improvements

    Visual FoxPro 9’s Report System has undergone a thorough revision. This topic sketches the broad outlines of the changes, and provides you with information about where to look for details.

    The following main areas of enhancements to the Report System are covered in sections of this topic. Design-time enhancements.

    Multiple features and changes make designing reports in Visual FoxPro better for you and your end-users. The Report Builder Application re-organizes your design experience out-of-the-box. If you want to customize the design process, Report Builder dialog boxes and Report Designer events are fully exposed for you to do so. Multiple detail bands.

    You can handle multiple child tables and data relationships more flexibly in the revised Report Designer. When you run multiple-detail-band reports, you can leverage the new bands, with associated detail headers and footers, both for appropriate presentation of these relationships and for more capable calculations. Object-assisted run-time report processing.

    An entirely re-built output system, including a new base class, changes the way Visual FoxPro provides output report and label files at run time. Object-assisted reporting provides better-quality output, new types of output, and an open-architecture based on a new Visual FoxPro base class, the ReportListener. A programmable Report Preview interface interacts with ReportListeners to give you full control over report preview experience. The Report Preview Application provides improved out-of-the-box previewing facilities. Printing, rendering, and character-set-handling improvements.

    Visual FoxPro 9 makes better use of the operating system’s printing features and GDI+ rendering subsystem. It also handles multiple locales and character sets better than previous versions. These changes are showcased in the Report System, and are accessible for use in custom code during report design and run-time processing. Extensible use of report and label definition files (.frx and .lbx tables).

    Visual FoxPro 9 handles your existing reports and labels without modification, while allowing you to add new features and behavior to these reports easily. This backward-compatible, yet forward-thinking, migration strategy is made possible by the Report System’s newly-flexible handling of the .frx and .lbx table structure.

    Design-time Enhancements

    Numerous changes in the Report System help you enhance the design-time experience for developers and end-users. This section directs you to information about design-time improvements.

    Report Designer Event Hooks and the Report Builder Application

    The Report Designer now offers Report Builder Hooks, which enable you to intercept events occurring during a report or label design session to override and extend designer activity. The default Report Builder Application replaces many of the standard reporting dialog boxes with new ones written in Visual FoxPro code. Components of the Report Builder Application are exposed as Visual FoxPro Foundation Classes for your use.

    To learn about:Read:
    Report Builder HooksUnderstanding Report Builder Events
    How the Report Builder Application uses Report Builder HooksHow to: Configure the Report Builder’s Event Handling
    How to specify and distribute a Report Builder with your applications_REPORTBUILDER System Variable How to: Specify and Distribute ReportBuilder.App Including Report Files for Distribution
    Using Report Builder algorithms in your codeFRX Cursor Foundation Class FRX Device Helper Foundation Class

    Protection for End-User Design Sessions, and other Design-time Customization Opportunities

    You can allow end-users to MODIFY and CREATE reports and labels, while setting limitations on what they can do in the Report Designer interface, using the new PROTECTED keyword. Protection is available individually by object and globally for the report. You can change what end-users see on the designer layout surface, from complex expressions to simple labels or sample data, while working in PROTECTED design mode, using Design-Time Captions. You can also provide helpful instructions, for both PROTECTED and standard design mode, by specifying Tooltips for report controls.

    To learn about:Read:
    Using the PROTECTED keywordMODIFY REPORT Command MODIFY LABEL Command
    Setting Protection in the Report or Label Designer, and what Protection settings doSetting Protection for Reports
    Protection settings exposed in Report or Label Dialog dialog boxes when you use the default Report Builder ApplicationProtection Tab, Report Control Properties Dialog Box (Report Builder) Protection Tab, Report Properties Dialog Box (Report Builder) Protection Tab, Report Band Properties Dialog Box (Report Builder)
    Design-Time CaptionsHow to: Add Design-time Captions to Field Controls
    ToolTips for Report ControlsHow to: Add Tooltips to Report Controls

    Enhanced Data Environment Use in Reports

    You can save the Data Environment you designed for a Report or Label as a visual class. You can load a Data Environment into a Report or Label design from either a visual class or a previously-saved report or label.

    To learn about:Read:
    Saving a Report Data EnvironmentHow to: Save Report Data Environments as Classes
    Loading a Report Data EnvironmentData Environment Tab, Report Properties Dialog Box (Report Builder) How to: Load Data Environments for Reports

    Miscellaneous Design Improvements

    There have been numerous enhancements to the Report and Label Designers. Some features are subtle changes to make design sessions more efficient and more enjoyable, and others improve your choices for resulting output.

    To learn about:Read:
    Improvements to the Report and Label Interactive Development Environment (IDE), such as: Enhanced Report Designer toolbar, and easier access to the Report Designer Toolbar from the View menu New global Report Properties context menu Improvements and additions to existing context menus Revised and extended Report menuReport Layout and Design
    Changes to global report and label design optionsReports Tab, Options Dialog Box
    Using the new PictureVal property of the Image control to specify images in reportsHow to: Add Pictures to Reports PictureVal Property
    New picture template characters (U and W) and updated format instructions (Z, now supported for date and datetime data), useful in reports and labelsFormat Expressions for Field Controls InputMask Property Format Property
    Receiving improved HTML output, which leverages run-time reporting enhancements, when you choose Save As HTML… while designing a report or labelHow to: Generate Output for Reports Tip: Other Visual FoxPro components that invoke Genhtml.prg, the default _GENHTML implementation, automatically share the improved HTML output, although these components have not changed. These include the FRX to HTML Foundation Class and the Output Object Foundation Class.
    Report document properties enable you to include information about the report in the report. Document properties are included as elements and attributes in XML and HTML output.How to: Add Document Properties to a Report Document Properties Tab, Report Properties Dialog Box (Report Builder)
    You can dynamically change the properties of report controls at run time based on the evaluation of an expression.How to: Dynamically Format Report Controls Dynamics Tab, Report Control Properties Dialog Box (Report Builder)

    Multiple Detail Bands

    The Report Engine can now move through a scope of records multiple times. The records can represent related sets of detail lines in child tables, or they can be multiple passes through a single table. These multiple passes through a scope of records are represented as multiple detail bands.

    Detail bands can have their own headers and footers, their own associated onEntry and onExit code, and their own associated report variables. Each detail band can be explicitly associated with a separate target alias, allowing you to control the number of entries in each detail band separately for related tables.

    Multiple detail band reports provide many new ways you can represent data in reports and labels, and new ways you can calculate or summarize data, as you move through a record scope.

    To learn about:Read:
    Designing reports and labels with multiple detail bands and their associated headers and footersOptional Bands Dialog Box Report Band Properties Dialog Box Band Tab, Report Band Properties Dialog Box (Report Builder)
    Handling multiple, related tables in report and label dataControlling Data in Reports Working with Related Tables using Multiple Detail Bands in Reports
    Associating report variables with detail bandsHow to: Reset Report Variables
    Comparing multiple groups and multiple detail bandsReport Bands

    Object-assisted Run-time Report Processing

    Visual FoxPro 9 has a new, object-assisted method of generating output from reports and labels. You can use your existing report and label layouts in object-assisted mode, to:

    • Generate multiple types of output during one report run.
    • Connect multiple reports together as part of one output result.
    • Improve the quality of traditional report output.
    • Dynamically adjust the contents of a report while you process it.
    • Provide new types of output not available from earlier versions of Visual FoxPro.

    This section covers the array of run-time enhancements that work together to support object-assisted reporting mode.

    Object-Assisted Architecture and ReportListener Base Class

    The new ReportListener base class and supporting language enhancements are the heart of run-time reporting enhancements.

    To learn about:Read:
    Fundamentals of the architecture, how its components work together, and what happens during an object-assisted report runUnderstanding Visual FoxPro Object-Assisted Reporting
    The ReportListener base class and its membersReportListener Object ReportListener Object Properties, Methods, and Events
    Invoking object-assisted reporting mode automaticallySET REPORTBEHAVIOR Command _REPORTOUTPUT System Variable Reports Tab, Options Dialog Box
    Invoking object-assisted reporting mode explicitly with Visual FoxPro commandsREPORT FORM Command LABEL Command
    Debugging and error-handling object-assisted report runsHandling Errors During Report Runs

    Report Preview API and the Report Preview Application

    Visual FoxPro 9’s object-assisted reporting mode gives you complete control over report and label previews.

    To learn about:Read:
    How object-assisted preview worksThe Preview Container API Creating a Custom Preview Container
    The default Report Preview ApplicationLeveraging the Default Preview Container
    How to specify and distribute Report Preview components with your applications_REPORTPREVIEW System Variable How to: Specify and Distribute ReportPreview.App Including Report Files for Distribution

    New Types of Output and the Report Output Component Set

    Because you can subclass ReportListener, you can create new types of output. Visual FoxPro 9 supplies a Report Output Application to connect ReportListener subclasses with output types, as well as ReportListener-derived classes with enhanced output capabilities.

    To learn about:Read:
    Requirements for Report Output Application, and how Visual FoxPro uses Report Output Applications_REPORTOUTPUT System Variable
    Features of the default Report Output ApplicationUnderstanding the Report Output Application
    Specifying custom output handlers using the default Report Output ApplicationHow to: Specify an Alternate Report Output Registry Table How to: Register Custom ReportListeners and Custom OutputTypes in the Report Output Registry Table Considerations for Creating New Report Output Types
    Understanding and configuring the Visual FoxPro Foundation Classes providing default ReportListener behavior for object-assisted preview and printingReportListener User Feedback Foundation Class
    Understanding and configuring the Visual FoxPro Foundation Classes responsible for default XML and HTML outputReportListener XML Foundation Class ReportListener HTML Foundation Class
    Leveraging the full set of supported Report Output Foundation Classes and VFP Report Output XML formatReportListener Foundation Classes Using VFP Report Output XML
    How to specify and distribute Report Output components with your applicationsHow to: Specify and Distribute Report Output Application Components Including Report Files for Distribution

    Migration Strategies and Changes in Output Rendering

    You can use the design-time changes to improve all reports and labels, whether you choose backward-compatible or object-assisted reporting mode at run time.

    When evaluating whether to switch to object-assisted reporting mode at run time, first consider items on the Reporting list of Important Changes in the Changes in Functionality for the Current Release topic, some of which are specific to this new method of creating output. .The topic includes a table of minor differences between backward-compatible and object-assisted reporting output. You can examine what effects these changes might have on individual existing reports, and use the recommendations in the table to address them. You will find additional details in the topic Using GDI+ in Reports.

    Once you have experimented with your current reports, you can decide on a migration strategy for output:

    • You can switch applications over to use object-assisted reporting mode completely, by using the command SET REPORTBEHAVIOR 90.
    • You can use SET REPORTBEHAVIOR 90 but preface specific REPORT FORM commands for reports with formatting issues with SET REPORTBEHAVIOR 80, returning your application to object-assisted mode afterwards.
    • You can use object-assisted mode all the time, but adjust your ReportListener-derived classes’ behavior to suit specific needs. For example, you could change the default setting of the ReportListener’s DynamicLineHeight Property to False (.F.).
    • You can leave SET REPORTBEHAVIOR at its default setting of 80, and add an explicit OBJECT clause to specific reports at your leisure, as you have the opportunity to evaluate and adjust individual report and label layouts.

    Printing, Rendering, and Character-set-handling Improvements

    General changes to Visual FoxPro’s use of Windows’ printing, rendering and font-handling support the improvements in the Report System’s output. These changes enhance your ability to support multiple printers and multiple languages in reports.

    To learn about:Read:
    GDI+ features and their impact on native Visual FoxPro outputUsing GDI+ in Reports
    Visual FoxPro reporting enhancements that allow your code to use GDI+ in object-assisted reporting mode, and Visual FoxPro Foundation Classes to get you startedGDIPlusGraphics Property Render Method GDI Plus API Wrapper Foundation Classes
    Making full use of multiple character sets, or language scripts, in reports, for single report layout elements, for report defaults, or globally in Visual FoxProGETFONT( ) Function Style Tab, Report Control Properties Dialog Box (Report Builder) How to: Change Page Settings for Reports Reports Tab, Options Dialog Box Reporting Features for International Applications
    Changes to page setup dialog boxes in Visual FoxPro, improvements in your programmatic access to them, and providing overrides to Printer Environment settings in report and label filesSYS(1037) – Page Setup Dialog Box
    Receiving improved information about the user’s installed printersAPRINTERS( ) Function
    Limiting a list of fonts to those appropriate for printer userGETFONT( ) Function

    Extensible Use of Report and Label Definition Files

    Underneath all the changes to the Visual FoxPro Report System, the Report Designer and Report Engine handle your report and label definitions using the same .frx and .lbx file structures as they did in previous versions. They change the way they use certain fields, without making these reports and labels invalid in previous versions, and they also allow you to extend your use of existing fields or add custom fields.

    Tip:
    This change is critical to your ability to create extensions of the new reporting features. For example, you might store two sets of ToolTips in two report extension fields, one set for use by developers and one for use by end-users. In a Report Builder extension, you could evaluate whether the Designer was working in protected or standard mode, and replace the actual set of ToolTips from the appropriate extension field. In previous versions, you could not add fields to report or label structure; the Designer and Engine would consider the table invalid. You also could not add custom content to unused, standard fields in various report and label records safely, because the Report Designer removed such content.

    Visual FoxPro 9 provides a revised FILESPEC table for report and label files, with extensive information on the use of each column in earlier versions as well as current enhancements.

    Visual FoxPro 9 also establishes a new, structured metadata format for use with reports. This format is an XML document schema shared with the Class Designer’s XML MemberData.

    The XML document format allows you to pack custom reporting information into a single report or label field. The default Report Builder Application makes it easy to add Report XML MemberData to report and label records.

    To learn about:Read:
    How Visual FoxPro uses .frx and .lbx tables, and how to extend these structuresUnderstanding and Extending Report Structure
    How to find and display the contents of the revised FILESPEC table, 60FRX.dbfTable Structures of Table Files (.dbc, .frx, .lbx, .mnx, .pjx, .scx, .vcx)
    How you can edit the XML data using the Report Builder ApplicationHow to: Assign Structured Metadata to Report Controls
    How you can use Report XML MemberDataReport XML MemberData Extensions
    The shared MemberData document schemaMemberData Extensibility

    See Also

    Concepts

    What’s New in Visual FoxPro

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    Data and XML Feature Enhancements

    Visual FoxPro contains the following additions and improvements to its data features:

    Extended SQL Capabilities

    Visual FoxPro contains many enhancements for SQL capabilities. For more information, see SQL Language Improvements.

    New Data Types

    Visual FoxPro includes the following new field and data types:

    Many of the Visual FoxPro language elements affected by these new data types are listed in the topics for the new data types.

    Binary Index Tag Based on Logical Expressions

    Visual FoxPro includes a new binary, or bitmap, index for creating indexes based on logical expressions, for example, indexes based on deleted records. A binary index can be significantly smaller than a non-binary index and can improve the speed of maintaining indexes. You can create binary indexes using the Table Designer or INDEX command. Visual FoxPro also includes Rushmore optimization enhancements in the SQL engine for deleted records.

    For more information, see Visual FoxPro Index Types, INDEX Command, ALTER TABLE – SQL Command, and Indexes Based on Deleted Records.

    Converting Data Types with the CAST( ) Function

    You can convert expressions from one data type to another by using the new CAST( ) function. Using CAST( ) makes it possible for you to create SQL statements more compatible with SQL Server.

    For more information, see CAST( ) Function.

    Get Cursor and Count Records Affected by SQL Pass-Thru Execution

    By using the aCountInfo parameter of the SQLEXEC( ) and SQLMORERESULTS( ) functions, you can get the name of the cursor created and a count of the records affected by the execution of a SQL pass-through statement.

    For more information, see SQLEXEC( ) Function) and SQLMORERESULTS( ) Function.

    Roll-Back Functionality Supported when a SQL Pass-Through Connection Disconnects

    Visual FoxPro now supports the DisconnectRollback property for use with the SQLSETPROP( ), SQLGETPROP( ), DBSETPROP( ), and DBGETPROP( ) functions. DisconnectRollback is a connection-level property that causes a transaction to be either rolled back or committed when the SQLDISCONNECT( ) function is called for the last connection handle associated with the connection. The DisconnectRollback property accepts a logical value. False (.F.) – (Default) The transaction will be committed when the SQLDISCONNECT( ) function is called for the last statement handle associated with the connection. True (.T.) – The transaction is rolled back when the SQLDISCONNECT( ) function is called for the last statement handle associated with the connection. The following example shows the DisconnectRollback property set in the DBSETPROP( ) and SQLSETPROP( ) functions.   Copy Code DBSETPROP(“testConnection”,”CONNECTION”,”DisconnectRollback”,.T.) SQLSETPROP(con,”DisconnectRollback”,.T.)

    For more information, see DisconnectRollback property in SQLSETPROP( ) Function.

    SQLIDLEDISCONNECT( ) Temporarily Disconnects SQL Pass-Through Connections

    You can use the new SQLIDLEDISCONNECT( ) function to allow a SQL Pass-Through connection to be temporarily disconnected. Use the following syntax.   Copy Code SQLIDLEDISCONNECT( nStatementHandle ) The nStatementHandle parameter is set to the statement handle to be disconnected or 0 if all statement handles should be disconnected. The SQLIDLEDISCONNECT( ) function returns the value 1 if it is successful; otherwise, it returns -1. The function fails if the specified statement handle is busy or the connection is in manual commit mode. The AERROR( ) function can be used to obtain error information. The disconnected connection handle is automatically restored if it is needed for an operation. The original connection data source name is used. If a statement handle is temporarily released, the OBDChstmt property returns 0; the OBDChdbc returns 0 if the connection is temporarily disconnected. A shared connection is temporarily disconnected as soon as all of its statement handles are temporarily released.

    For more information, see SQLIDLEDISCONNECT( ) Function.

    Retrieving Active SQL Connection Statement Handles

    You can retrieve information for all active SQL connection statement handles using the new ASQLHANDLES( ) function. ASQLHANDLES( ) creates and uses the specified array to store numeric statement handle references that you can use in other Visual FoxPro SQL functions, such as SQLEXEC( ) and SQLDISCONNECT( ). ASQLHANDLES( ) returns the number of active statement handles in use or zero (0) if none are available. For more information, see ASQLHANDLES( ) Function.

    Obtain the ADO Bookmark for the Current Record in an ADO-Based Cursor

    The ADOBookmark property is now supported by the CURSORGETPROP( ) function. Use this property to obtain the ActiveX® Data Objects (ADO) bookmark for the current record in an ADO-based cursor.

    For more information, see ADOBookmark Property in CURSORGETPROP( ) Function.

    If a table is not selected and an alias is not specified, Error 52, “No table is open in the current work area,” is generated. If the cursor selected is not valid, Error 1467, “Property is invalid for local cursors,” is generated.

    Obtain the Number of Fetched Records

    You can obtain the number of fetched records during SQL Pass-Through execution by using the new RecordsFetched cursor property with the CURSORGETPROP( ) function. Specifying the RecordsFetched cursor property will return the number of fetched records from an OBDC/ADO-based cursor. If records have been deleted or appended locally, the RecordsFetched cursor property may not return the current number of records in the OBDC/ADO-based cursor. In addition, filter conditions are ignored.

    For more information, see RecordsFetched Property in CURSORGETPROP( ) Function.

    Determine if a Fetch is Complete

    You can determine if a fetch process is complete for an OBDC/ADO-based cursor by using the new FetchIsComplete cursor property with the CURSORGETPROP( ) function. Read-only at design time and run time. This property is not supported on environment level (work area 0) cursors, tables, and local views. The FetchIsComplete cursor property returns a logical expression True (.T.) if the fetch process is complete; otherwise False (.F.) is returned.

    For more information, see FetchIsComplete Property in CURSORGETPROP( ) Function.

    ISMEMOFETCHED( ) Determines Whether a Memo is Fetched

    You can use the ISMEMOFETCHED( ) function to determine whether a Memo field or General field is fetched when you are using delayed memo fetching. For more information about delayed memo fetching, see Speeding Up Data Retrieval.

    The syntax for this function is:

    ISMEMOFETCHED(cFieldName | nFieldNumber [, nWorkArea | cTableAlias ])

    The ISMEMOFETCHED( ) function returns True (.T.) when the Memo field is fetched or if local data is used. ISMEMOFETCHED() returns NULL if the record pointer is positioned at the beginning of the cursor or past the last record.

    For more information, see ISMEMOFETCHED( ) Function.

    Cancel ADO Fetch

    In Visual FoxPro, you can now cancel a lengthy ADO fetch by pressing the ESC key.

    Long Type Name Support

    Visual FoxPro supports using long type names with the following functions, commands, and properties.

    The following table lists the data types along with their long type names and short type names.

    Data TypeLong Type NameShort Type Name
    CharacterChar, CharacterC
    DateDateD
    DateTimeDatetimeT
    NumericNum, NumericN
    FloatingFloatF
    IntegerInt, IntegerI
    DoubleDoubleB
    CurrencyCurrencyY
    LogicalLogicalL
    MemoMemoM
    GeneralGeneralG
    PicturePictureP
    VarcharVarcharV
    VarbinaryVarbinaryQ
    BlobBlobW

    Visual FoxPro allows ambiguous long type names to be used with the ALTER TABLE, CREATE CURSOR, CREATE TABLE, and CREATE FROM commands. If the specified long type name is not a recognized long type name, Visual FoxPro will truncate the specified name to the first character.

    Transaction Support for Free Tables and Cursors

    In prior versions of Visual FoxPro, transactions using the BEGIN TRANSACTION Command were only supported for local and remote data from databases. Transactions involving free tables and cursors are now supported through use of the MAKETRANSACTABLE( ) and ISTRANSACTABLE( ) functions. For more information, see MAKETRANSACTABLE( ) Function and ISTRANSACTABLE( ) Function.

    Specify a Code Page When Using the CREATE TABLE or CREATE CURSOR Commands

    You can specify a code page by including the CODEPAGE clause with the CREATE CURSOR or CREATE TABLE commands. When the CODEPAGE clause is specified, the new table or cursor has a code page specified by nCodePage. An error, 1914, “Code page number is invalid”, is generated if an invalid code page is specified. The following example creates a table and displays its code page:   Copy Code CREATE TABLE Sales CODEPAGE=1251 (OrderID I, CustID I, OrderAmt Y(4)) ? CPDBF( )

    For more information, see CREATE CURSOR – SQL Command, CREATE TABLE – SQL Command and Code Pages Supported by Visual FoxPro.

    Convert Character and Memo Data Types Using the ALTER TABLE Command

    Visual FoxPro now supports automatic conversion from character data type to memo data type without loss of data when using the ALTER TABLE command along with the ALTER COLUMN clause. This conversion is also supported when making structural changes using the Table Designer. For more information, see ALTER TABLE – SQL Command.

    BLANK Command Can Initialize Records to Default Value

    You can initialize fields in the current record to their default values as stored in the table’s database container (DBC) by using the DEFAULT [AUTOINC] option when clearing the record with the BLANK command. For more information, see BLANK Command.

    FLUSH Command Writes Data Explicitly to Disk

    Visual FoxPro now includes options and parameters for the FLUSH command and FFLUSH function so you can explicitly save all changes you make to all open tables and indexes. You can also save changes to a specific table by specifying a work area, table alias, or a path and file name. For more information, see FLUSH Command and FFLUSH( ) Function.

    Populate an Array with Aliases Used by a Specified Table

    The new cTableName parameter for the AUSED( ) function makes it possible to filter the created array to contain only the aliases being used for a specified table. AUSED(ArrayName [, nDataSessionNumber [, cTableName ]]) The cTableName parameter accepts the following formats to specify a table, from highest to lowest in priority. DatabaseName!TableName or DatabaseName!ViewName Path\DatabaseName!TableName or Path\DatabaseName!ViewName DBC-defined table name or view in the current DBC in the current data session Simple or full file name

    For more information, see AUSED( ) Function.

    Obtain Last Auto-Increment Value with GETAUTOINCVALUE( )

    You can use the new GETAUTOINCVALUE( ) function to return the last value generated for an autoincremented field within a data session. For more information, see GETAUTOINCVALUE( ) Function.

    SET TABLEPROMPT Controls Prompt to Select Table

    The new SET TABLEPROMPT command controls whether Visual FoxPro prompts the user with the Open Dialog Box (Visual FoxPro) to select a table when one specified cannot be found, such as in SELECT – SQL Command. For more information, see SET TABLEPROMPT Command.

    Use SET VARCHARMAPPING to Control Query Result Set Mappings

    For queries such as SELECT – SQL Command, character data is often manipulated using Visual FoxPro functions and expressions. Since the length of the resulting field value may be important for certain application uses, it is valuable to have this Character data mapped to Varchar data in the result set. The SET VARCHARMAPPING command controls whether Character data is mapped to a Character or Varchar data type. For more information, see SET VARCHARMAPPING Command.

    SET TABLEVALIDATE Expanded

    When a table header is locked during validation, attempts to open the table, for example, with the USE command, generate the message “File is in use (Error 3).” If the table header cannot be locked for a table open operation, you can suppress this message by setting the third bit for the SET TABLEVALIDATE command. You must also set the first bit to validate the record count when the table opens. Therefore, you need to set the SET TABLEVALIDATE command to a value of 5. Also, a fourth bit option (value of 8) is available for Insert operations which checks the table header before the appended record is saved to disk and the table header is modified.

    For more information, see SET TABLEVALIDATE Command.

    SET REFRESH Can Specify Faster Refresh Rates

    You can specify fractions of a second for the nSeconds2 parameter to a minimum of 0.001 seconds. You can also specify the following values for the optional second parameter: -1 – Always read data from a disk. 0 – Always use data in memory buffer but do not refresh buffer. The Table refresh interval check box on the Data tab of the Options dialog box now also accepts fractional values.

    For more information, see SET REFRESH Command and Data Tab, Options Dialog Box.

    SET REFRESH Can Differentiate Values for Each Cursor

    You can use the new Refresh property with the CURSORGETPROP( ) function to differentiate the SET REFRESH values for individual cursors. The default setting is -2, which is a global value. This value is not available with the SET REFRESH command. The Refresh property is available at the Data Session and Cursor level. The default setting for a Data Session level is -2 and the default value for a Cursor level is the current session’s level setting. If the global level setting is set to 0, the Cursor level setting is ignored. If a table is not currently selected and an alias is not specified, Error 52, “No table is open in the current work area,” is generated.

    For more information, see Refresh Property in CURSORGETPROP( ) Function.

    SET( ) Determines SET REPROCESS Command Settings

    You can now use the following syntax with the SET( ) function to determine how the SET REPROCESS command was declared. SET Command Value Returned REPROCESS, 2 Current session setting type (0 – attempts, 1 – seconds) REPROCESS, 3 System session setting type (0 – attempts, 1 – seconds)

    For more information, see SET( ) Function and SET REPROCESS Command.

    Log Output from SYS(3054) Using SYS(3092)

    You can use the new SYS(3092) function in conjunction with SYS(3054) to record the resulting output to a file. SYS( 3092 [, cFileName [, lAdditive ]]) The cFileName parameter specifies the file to echo the SYS(3054) output to. Sending an empty string to cFileName will deactivate output recording to the file. The default value for lAdditive is False (.F.). This specifies that new output will overwrite the previous contents of the specified file. To append new output to the specified file, set lAdditive to True (.T.). SYS(3092) returns the name of the current echo file if it is active; otherwise, it returns an empty string. SYS(3054) and SYS(3092) are global settings — in a multithreaded runtime they are scoped to a thread. Each function can be changed independently from each other. These functions are not available in the Visual FoxPro OLE DB Provider.

    For more information, see SYS(3054) – Rushmore Query Optimization Level and SYS(3092) – Output Rushmore Query Optimization Level.

    Purge Cached Memory for Specific Work Area Using SYS(1104)

    You can optionally specify the alias or work area of a specified table or cursor for which cached memory is purged. For more information, see SYS(1104) – Purge Memory Cache.

    New Table Types for SYS(2029)

    The SYS(2029) function returns new values for tables that contain Autoinc, Varchar, Varbinary or Blob fields. For more information, see SYS(2029) – Table Type.

    Map Remote Unicode Data to ANSI Using SYS(987)

    Use SYS(987) to map remote Unicode data retrieved through SQL pass-through or remote views to ANSI. This function can be used to retrieve remote Varchar data as ANSI for use with Memo fields. This setting is a global setting across all data sessions so should be used with care. For more information, see SYS(987) – Map Remote Data to ANSI.

    Memo and Field tips in a BROWSE or Grid

    When the mouse pointer is positioned over a Memo field cell in a Browse window or Grid control, a Memo Tip window displays the contents of the Memo field. For other field types, positioning the mouse pointer over the field displays the field contents in a Field Tip window when the field is sized smaller than its contents.

    Memo Tip windows display no more than 4 kilobytes of text, and are not displayed for binary data. A Memo Tip window is displayed until the mouse pointer is moved from the Memo field. The _TOOLTIPTIMEOUT System Variable determines how long a Field Tip window is displayed.

    You can disable Memo Tips by setting the _SCREEN ShowTips Property to False (.F.).

    Memo and Field Tips will also be displayed for Grid controls if both _SCREEN and the form’s ShowTips property are set to True (.T.). Additionally, the ToolTipText Property for the field’s grid column Textbox control must contain an empty string.

    Specify Code Pages

    You can specify the code page used to decode data when XML is being parsed and to encode data when UTF-8 encoded XML is generated. The following language changes are available:

    For more information, see Code Pages Supported by Visual FoxPro.

    MapVarchar Property Maps to Varchar, Varbinary, and Blob Data Types

    For CursorAdapter and XMLAdapter classes, you can use the MapVarchar property to map to Varchar data types. To map to Varbinary and Blob data types, you can use the MapBinary property.

    The XMLTOCURSOR( ) Function contains several new flags to support mapping of Char and base64Binary XML field types to new Fox data types.

    For more information, see the MapVarchar Property and MapBinary Property.

    Handling Conflict Checks with Properties for CursorAdapter Class

    You can better handle conflicts when performing update and delete operations using the commands specified by the UpdateCmd and DeleteCmd properties for CursorAdapter objects by using the new ConflictCheckType and ConflictCheckCmd properties for CursorAdapter objects. You can use ConflictCheckType to specify how to handle a conflict check during an update or delete operation. When ConflictCheckType is set to 4, you can use ConflictCheckCmd to specify a custom command to append to the end of the commands in the UpdateCmd and DeleteCmd properties. Note: Visual FoxPro 8.0 Service Pack 1 includes the ConflictCheckType and ConflictCheckCmd properties.

    For more information, see ConflictCheckType Property and ConflictCheckCmd Property.

    Improved DataEnvironment Handling with UseCursorSchema and NoData Properties

    You can specify default settings for CursorFill Method calls made without the first two parameters by setting these properties. For more information, see UseCursorSchema Property and NoData Property.

    Timestamp Field Support

    The new TimestampFieldList property lets you specify a list of timestamp fields for the cursor created by the CursorAdapter. For more information see TimestampFieldList Property.

    Auto-Refresh Support

    There are a number of scenarios where you might want to have cursor data refreshed from a remote data source after an Insert/Update operation has occurred. These include following scenarios: A table has an auto-increment field that also acts as a primary key. A table has a timestamp field, and that field must be refreshed from the database after each Insert/Update in order to allow successful subsequent updates to the record when WhereType=4 (key and timestamp). A table contains some fields which have DEFAULT values or triggers defined that will cause changes to occur. The following new properties have been added to the CursorAdapter class for Auto-Refresh support: Property Description InsertCmdRefreshFieldList List of fields to refresh after Insert command executes. InsertCmdRefreshCmd Specifies the command to refresh the record after Insert command executes. InsertCmdRefreshKeyFieldList List of key fields to refresh in record after Insert command executes. UpdateCmdRefreshFieldList List of fields to refresh after Update command executes. UpdateCmdRefreshCmd Specifies the command to refresh the record after Update command executes. UpdateCmdRefreshKeyFieldList List of key fields to refresh the record after Update command executes. RefreshTimestamp Enables automatic refresh for fields in TimestampFieldList during Insert/Update.

    For more information about how Visual FoxPro updates remote data using a CursorAdapter, see Data Access Management Using CursorAdapters. Also, see InsertCmdRefreshCmd Property, InsertCmdRefreshFieldList Property, InsertCmdRefreshKeyFieldList Property, UpdateCmdRefreshCmd Property, UpdateCmdRefreshFieldList Property, UpdateCmdRefreshKeyFieldList Property and RefreshTimeStamp Property.

    On Demand Record Refresh

    In Visual FoxPro 8.0, the REFRESH( ) Function provides on demand record refresh functionality for local and remote views, however, it does not support this for the CursorAdapter. Visual FoxPro 9.0 extends REFRESH( ) support to the CursorAdapter and provides some additional capabilities:

    MemberDescription
    RecordRefresh methodRefreshes the current field values for the target records. Use the CURVAL( ) Function to determine current field values.
    BeforeRecordRefresh eventOccurs immediately before the RecordRefresh method is executed.
    AfterRecordRefresh eventOccurs after the RecordRefresh method is executed.
    RefreshCmdDataSourceType propertySpecifies the data source type to be used for the RecordRefresh method.
    RefreshCmdDataSource propertySpecifies the data source to be used for the RecordRefresh method.
    RefreshIgnoreFieldList propertyList of fields to ignore during RecordRefresh operation
    RefreshCmd propertySpecifies the command to refresh rows when RecordRefresh is executed.
    RefreshAlias propertySpecifies the alias of read-only cursor used as a target for the refresh operation.

    For more information, see RecordRefresh Method, BeforeRecordRefresh Event, AfterRecordRefresh Event, RefreshCmdDataSourceType Property, RefreshCmdDataSource Property, RefreshIgnoreFieldList Property, RefreshCmd Property and RefreshAlias Property.

    Delayed Memo Fetch

    The CursorAdapter class has a FetchMemo Property, which when set to False (.F.) in Visual FoxPro 9.0 places the cursor in Delayed Memo Fetch mode similar to Remote Views. Delayed Memo Fetch Mode prevents the contents of Memo fields from being fetched using CursorFill Method or CursorRefresh Method. An attempt to fetch content for a Memo field is done when the application attempts to access the value. The following CursorAdapter enhancements provide support for Delayed Memo Fetch:

    MemberDescription
    DelayedMemoFetch methodPerforms a delayed Memo field fetch for a target record in a cursor in a CursorAdapter object.
    FetchMemoDataSourceType propertySpecifies the data source type used for the DelayedMemoFetch method.
    FetchMemoDataSource propertySpecifies the data source used for the DelayedMemoFetch method.
    FetchMemoCmdList propertySpecifies a list of Memo field names and their associated fetch commands.

    For more information, see DelayedMemoFetch Method, FetchMemoDataSourceType Property, FetchMemoDataSource Property and FetchMemoCmdList Property.

    UseTransactions Property

    The new UseTransactions property specifies whether the CursorAdapter should use transactions when sending Insert, Update or Delete commands through ADO or ODBC. For more information, see UseTransactions Property.

    DEFAULT and CHECK Constraints Respected

    In Visual FoxPro 9.0, DEFAULT values and table and field level CHECK constraints are supported for XML, Native, ADO and ODBC data sources. In Visual FoxPro 8.0, DEFAULT values and table and field level CHECK constraints are only supported for an XML data source. For the DEFAULT values and CHECK constraints to be applied to a cursor, call the CursorFill Method with the lUseSchema parameter set to True (.T.). For more information, see CursorSchema Property.

    Remote Data Type Conversion for Logical Data

    When you move data between a remote server and Visual FoxPro, Visual FoxPro uses ODBC or ADO data types to map remote data types to local Visual FoxPro data types. In Visual FoxPro 9.0, certain ODBC and ADO data types can now be mapped to a logical data type in remote views and the CursorAdapter object. For more information, see Data Type Conversion Control.

    ADOCodePage Property

    When working with an ADO data source for your CursorAdapter, you may want to specify a code page to use for character data translation. The new ADOCodePage property allows you to specify this code page. For more information, see ADOCodePage Property.

    Read and Write Nested XML Documents

    You can read to and write from your relational database into XML documents using nesting to handle the relationships between tables. You accomplish this using the RespectNesting Property of the XMLAdapter class. The XMLTable class has the Nest Method, Unnest Method and the following properties to handle nesting.

    For more information, see the XMLAdapter Class and the XMLTable Class.

    LoadXML Method Can Accept Any XML Document

    The LoadXML method accepts any XML document with a valid schema. Previously, the method required that the schema follow the format of a Visual Studio generated dataset. When you use the LoadXML method to read an XML document with a schema different from a Visual Studio generated dataset, the properties for the XMLAdapter, XMLName, and XMLPrefix properties are set to empty (“”). The XMLAdapter XMLNamespace property becomes equal to the target Namespace attribute value for the schema node and each XML element becomes a complexType and is mapped to an XMLTable object. The XMLNamespace property is set to namespaceURI for the element. If you set the XMLAdapter RespectNesting property to True (.T.), the top level element declaration is ignored if it is referenced from some other complex element. For that case, the XMLTable object for the referenced element is nested into the XMLTable for the element that references it.

    For more information, see LoadXML Method.

    XPath Expressions Can Access Complex XML Documents

    You can use XPath expressions to access complex XML documents and the new properties for reading the nodes within the document. For example, you might want to filter record nodes, restore relationships based on foreign key fields, use an element’s text as data for a field, or access XML that uses multiple XML namespaces. The following properties provide you with the ability to read the XML at the XMLAdapter level, XMLTable level, or the XMLField level.

    You can use the following table to determine the node within the XML document that you want to start reading.

    For example, if you use an XPath expression in the XMLName property for an XMLAdapter, reading begins at the first node

    To readClassContext node
    From the first found XML node:XMLAdapterIXMLDOMElement property
    All found XML nodes and use each node as a single record:XMLTableXMLAdapter object
    The first found XML node and use its text as a field value:XMLFieldXMLTable object

    The following methods do not support the use of XPath expressions in the XMLName property:

    • The ApplyDiffgram and ChangesToCursor methods do not support XPath expressions for XMLAdapter and XMLTable objects.
    • The ToCursor method does not support an XPath expression for XMLAdapter when the IsDiffgram property is set to True (.T.).
    • The ToXML method does not support XPath expressions for XMLAdapter and XMLTable objects and ignores XMLField objects that use XPath expressions.

    For more information about XPath expressions, see the XPath Reference in the Microsoft Core XML Services (MSXML) 4.0 SDK in the MSDN library at http://msdn.microsoft.com/library.

    Cursor to XML Functions

    Support for the following functions has been added to the OLE DB Provider for Visual FoxPro:

    When used in the OLE DB Provider for Visual FoxPro, the _VFP VFPXMLProg property is not supported for the CURSORTOXML( ), XMLTOCURSOR( ) and XMLUPDATEGRAM( ) functions because the _VFP system variable is not supported in the OLE DB Provider.

    EXECSCRIPT Supported in the Visual FoxPro OLE DB Provider

    You can use the EXECSCRIPT( ) function with the Visual FoxPro OLE DB Provider. For more information, see EXECSCRIPT( ) Function.

    Returning a Rowset from a Cursor in the Visual FoxPro OLE DB Provider

    You can use the new SETRESULTSET( ), GETRESULTSET( ), and CLEARRESULTSET( ) functions to mark a cursor or table that has been opened by the Visual FoxPro OLE DB Provider, retrieve the work area of the marked cursor, and clear the marker flag from a marked cursor. By marking a cursor or table, you can retrieve a rowset that is created from the marked cursor or table from a database container (DBC) stored procedure when the OLE DB Provider completes command execution.

    For more information, see SETRESULTSET( ) Function, GETRESULTSET( ) Function, and CLEARRESULTSET( ) Function.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    SQL Language Improvements

    The SELECT – SQL Command and other SQL commands have been substantially enhanced in Visual FoxPro 9.0. This topic describes the enhancements made to these commands, and new commands that affect SQL performance.

    Expanded Capacities

    Several SELECT – SQL command limitations have been removed or increased in Visual FoxPro 9.0. The following table lists the areas where limitations have been removed or increased.

    CapacityDescription
    Number of Joins and Subqueries in a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on the total number of join clauses and subqueries in a SELECT – SQL command. The previous limit was nine.
    Number of UNION clauses in a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on number of UNION clauses in a SQL SELECT statement. The previous limit was nine.
    Number of tables referenced a SELECT – SQL commandVisual FoxPro 9.0 removes the limit on the number of tables and aliases referenced in a SQL SELECT statement. The previous limit was 30.
    Number of arguments in an IN( ) clauseVisual FoxPro 9.0 removes the limit of 24 values in the IN (Value_Set) clause for the WHERE clause. However, the number of values remains subject to the setting of SYS(3055) – FOR and WHERE Clause Complexity. For functionality changes concerning the IN clause, see Changes in Functionality for the Current Release.

    Subquery Enhancements

    Visual FoxPro 9.0 provides more flexibility in subqueries. For example, multiple subqueries are now supported. The following describes the enhancements to subqueries in Visual FoxPro 9.0.

    Multiple Subqueries

    Visual FoxPro 9.0 supports multiple subquery nesting, with correlation allowed to the immediate parent. There is no limit to the nesting depth. In Visual FoxPro 8.0, error 1842 (SQL: Subquery nesting is too deep) was generated when more than one level of subquery nesting occurred.

    The following is the general syntax for multiple subqueries.

    SELECT … WHERE … (SELECT … WHERE … (SELECT …) …) …

    Examples

    The following example queries, which will generate an error in Visual FoxPro 8.0, are now supported in Visual FoxPro 9.0.

     Copy Code
    CREATE CURSOR MyCursor (field1 I) INSERT INTO MyCursor VALUES (0) CREATE CURSOR MyCursor1 (field1 I) INSERT INTO MyCursor1 VALUES (1) CREATE CURSOR MyCursor2 (field1 I) INSERT INTO MyCursor2 VALUES (2) SELECT * FROM MyCursor T1 WHERE EXISTS ; (SELECT * from MyCursor1 T2 WHERE NOT EXISTS ; (SELECT * FROM MyCursor2 T3)) *** Another multiple subquery nesting example *** SELECT * FROM table1 WHERE table1.iid IN ; (SELECT table2.itable1id FROM table2 WHERE table2.iID IN ; (SELECT table3.itable2id FROM table3 WHERE table3.cValue = “value”))

    GROUP BY in a Correlated Subquery

    Many queries can be evaluated by executing a subquery once and substituting the resulting value or values into the WHERE clause of the outer query. In queries that include a correlated subquery (also known as a repeating subquery), the subquery depends on the outer query for its values. This means that the subquery is executed repeatedly, once for each row that might be selected by the outer query.

    Visual FoxPro 8.0 does not allow using GROUP BY in correlated subquery, and generates error 1828 (SQL: Illegal GROUP BY in subquery). Visual FoxPro 9.0 removes this limitation and supports GROUP BY for correlated subqueries allowed to return more than one record.

    The following is the general syntax for the GROUP BY clause in a correlated subquery.

    SELECT … WHERE … (SELECT … WHERE … GROUP BY …) …

    Examples

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor1 (field1 I, field2 I, field3 I) INSERT INTO MyCursor1 VALUES(1,2,3) CREATE CURSOR MyCursor2 (field1 I, field2 I, field3 I) INSERT INTO MyCursor2 VALUES(1,2,3) SELECT * from MyCursor1 T1 WHERE field1; IN (SELECT MAX(field1) FROM MyCursor2 T2 ; WHERE T2.field2=T1.FIELD2 GROUP BY field3)

    TOP N in a Non-Correlated Subquery

    Visual FoxPro 9.0 supports the TOP N clause in a non-correlated subquery. The ORDER BY clause should be present if the TOP N clause is used, and this is the only case where it is allowed in subquery.

    The following is the general syntax for the TOP N clause in a non-correlated subquery.

    SELECT … WHERE … (SELECT TOP nExpr [PERCENT] … FROM … ORDER BY …) …

    Examples

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor1 (field1 I, field2 I, field3 I) INSERT INTO MyCursor1 VALUES(1,2,3) CREATE CURSOR MyCursor2 (field1 I, field2 I, field3 I) INSERT INTO MyCursor2 VALUES(1,2,3) SELECT * FROM MyCursor1 WHERE field1 ; IN (SELECT TOP 5 field2 FROM MyCursor2 order by field2)

    Subqueries in a SELECT List

    Visual FoxPro 9.0 allows a subquery as a column or a part of expression in a projection. A subquery in a projection has exactly the same requirements as a subquery used in a comparison operation. If a subquery does not return any records, NULL value is returned.

    In Visual FoxPro 8.0, an attempt to use a subquery as a column or a part of expression in a projection would generate error 1810 (SQL: Invalid use of subquery).

    The following is the general syntax for a subquery in a SELECT list.

    SELECT … (SELECT …) … FROM …

    Example

    The following example, which will generate an error in Visual FoxPro 8.0, is now supported in Visual FoxPro 9.0.

     Copy Code
    SELECT T1.field1, (SELECT field2 FROM MyCursor2 T2; WHERE T2.field1=T1.field1) FROM MyCursor1 T1

    Aggregate functions in a SELECT List of a Subquery

    In Visual FoxPro 9.0, aggregate functions are now supported in a SELECT list of a subquery compared using the comparison operators <, <=, >, >= followed by ALL, ANY, or SOME. See Considerations for SQL SELECT Statements for more information about aggregate functions.

    Example

    The following example demonstrates the use of an aggregate function (the COUNT( ) function) in a SELECT list of a subquery.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor (FIELD1 i) INSERT INTO MyCursor VALUES (6) INSERT INTO MyCursor VALUES (0) INSERT INTO MyCursor VALUES (1) INSERT INTO MyCursor VALUES (2) INSERT INTO MyCursor VALUES (3) INSERT INTO MyCursor VALUES (4) INSERT INTO MyCursor VALUES (5) INSERT INTO MyCursor VALUES (-1) CREATE CURSOR MyCursor2 (FIELD2 i) INSERT INTO MyCursor2 VALUES (1) INSERT INTO MyCursor2 VALUES (2) INSERT INTO MyCursor2 VALUES (2) INSERT INTO MyCursor2 VALUES (3) INSERT INTO MyCursor2 VALUES (3) INSERT INTO MyCursor2 VALUES (3) INSERT INTO MyCursor2 VALUES (4) INSERT INTO MyCursor2 VALUES (4) INSERT INTO MyCursor2 VALUES (4) INSERT INTO MyCursor2 VALUES (4) SELECT * FROM MYCURSOR WHERE field1; < ALL (SELECT count(*) FROM MyCursor2 GROUP BY field2) ; INTO CURSOR MyCursor3 BROWSE

    Correlated Subqueries Allow Complex Expressions to be Compared with Correlated Field

    In Visual FoxPro 8.0, correlated fields can only be referenced in the following forms:

    correlated field <comparison> local field

    -or-

    local field <comparison> correlated field

    In Visual FoxPro 9.0. correlated fields support comparison to local expressions, as shown in the following forms:

    correlated field <comparison> local expression

    -or-

    local expression <comparison> correlated field

    A local expression must use at least one local field and cannot reference any outer (correlated) field.

    Example

    In the following example, a local expression (MyCursor2.field2 / 2) is compared to a correlated field (MyCursor.field1).

     Copy Code
    SELECT * FROM MyCursor ; WHERE EXISTS(SELECT * FROM MyCursor2 ; WHERE MyCursor2.field2 / 2 > MyCursor.field1)

    Changes for Expressions Compared with Subqueries.

    In Visual FoxPro 8.0, the left part of a comparison using the comparison operators [NOT] IN, <, <=, =, ==, <>, !=, >=, >, ALL, ANY, or SOME with a subquery must reference one and only one table from the FROM clause. In case of a comparison with correlated subquery, the table must also be the correlated table.

    In Visual FoxPro 9.0, comparisons work in the following ways:

    • The expression on the left side of an IN comparison must reference at least one table from the FROM clause.
    • The left part for the conditions =, ==, <>, != followed by ALL, SOME, or ANY must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (SELECT TOP…) must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (SELECT <aggregate function>…) must reference at least one table from the FROM clause.
    • The left part for the condition >, >=, <, <= followed by ALL, SOME, or ANY (subquery with GROUP BY and/or HAVING) must reference at least one table from the FROM clause.

    In Visual FoxPro 9.0, the left part of a comparison that does not come from the list (for example, ALL, SOME, or ANY are not included) doesn’t have to reference any table from the FROM clause.

    In all cases, the left part of the comparison is allowed to reference more than one table from the FROM clause. For a correlated subquery, the left part of the comparison does not have to reference the correlated table.

    Subquery in an UPDATE – SQL Command SET List

    In Visual FoxPro 9.0, the UPDATE – SQL Command now supports a subquery in the SET clause.

    A subquery in a SET clause has exactly the same requirements as a subquery used in a comparison operation. If the subquery does not return any records, the NULL value is returned.

    Only one subquery is allowed in a SET clause. If there is a subquery in the SET clause, subqueries in the WHERE clause are not allowed.

    The following is the general syntax for a subquery in the SET clause.

    UPDATE … SET … (SELECT …) …

    Example

    The following example demonstrates the use of a subquery in the SET clause.

     Copy Code
    CLOSE DATA CREATE CURSOR MyCursor1 (field1 I , field2 I NULL) INSERT INTO MyCursor1 VALUES (1,1) INSERT INTO MyCursor1 VALUES (2,2) INSERT INTO MyCursor1 VALUES (5,5) INSERT INTO MyCursor1 VALUES (6,6) INSERT INTO MyCursor1 VALUES (7,7) INSERT INTO MyCursor1 VALUES (8,8) INSERT INTO MyCursor1 VALUES (9,9) CREATE CURSOR MyCursor2 (field1 I , field2 I) INSERT INTO MyCursor2 VALUES (1,10) INSERT INTO MyCursor2 VALUES (2,20) INSERT INTO MyCursor2 VALUES (3,30) INSERT INTO MyCursor2 VALUES (4,40) INSERT INTO MyCursor2 VALUES (5,50) INSERT INTO MyCursor2 VALUES (6,60) INSERT INTO MyCursor2 VALUES (7,70) INSERT INTO MyCursor2 VALUES (8,80) UPDATE MyCursor1 SET field2=100+(SELECT field2 FROM MyCursor2 ; WHERE MyCursor2.field1=MyCursor1.field1) WHERE field1>5 SELECT MyCursor1 LIST

    Sub-SELECT in the FROM Clause

    A sub-SELECT is often referred to as a derived table. Derived tables are SELECT statements in the FROM clause referred to by an alias or a user-specified name. The result set of the SELECT in the FROM clause creates a table used by the outer SELECT statement. Visual FoxPro 9.0 permits the use of a subquery in the FROM clause.

    A sub-SELECT should be enclosed in parentheses and an alias is required. Correlation is not supported. A sub-SELECT has the same syntax limitations as the SELECT command, but not the subquery syntax limitations. All sub-SELECTs are executed before the top most SELECT is evaluated.

    The following is the general syntax for a subquery in the FROM clause.

    SELECT … FROM (SELECT …) [AS] Alias…

    Example

    The following example demonstrates the use of a subquery in the FROM clause.

     Copy Code
    SELECT * FROM (SELECT * FROM MyCursor T1; WHERE field1 = (SELECT T2.field2 FROM MyCursor1 T2; WHERE T2.field1=T1.field2); UNION SELECT * FROM MyCursor2; ORDER BY 2 desc) AS subquery *** Note that the following code will generate an error *** SELECT * FROM (SELECT TOP 5 field1 FROM MyCursor) ORDER BY field1

    ORDER BY with Field Names in the UNION clause

    When using a UNION clause in Visual FoxPro 8.0, you are forced to use numeric references in the ORDER BY clause. In Visual FoxPro 9.0, this restriction has been removed and you can use field names.

    The referenced fields must be present in the SELECT list (projection) for the last SELECT in the UNION; that projection is used for ORDER BY operation.

    Example

    The following example demonstrates the use of a field names in the ORDER BY clause.

     Copy Code
    CLOSE DATABASES all CREATE CURSOR MyCursor(field1 I,field2 I) INSERT INTO MyCursor values(1,6) INSERT INTO MyCursor values(2,5) INSERT INTO MyCursor values(3,4) SELECT field1, field2, .T. AS FLAG,1 FROM MyCursor; WHERE field1=1; UNION ; SELECT field1, field2, .T. AS FLAG,1 FROM MyCursor; WHERE field1=3; ORDER BY field2 ; INTO CURSOR TEMP READWRITE BROWSE NOWAIT

    Optimized TOP N Performance

    In Visual FoxPro 8.0 and earlier versions, when using the TOP N [PERCENT] clause all records are sorted and then the TOP N are extracted. In Visual FoxPro 9.0, performance has been improved by eliminating records that do not qualify for the TOP N from the sort process as early as possible.

    The TOP N optimization is done only if the SET ENGINEBEHAVIOR Command is set to 90.

    Optimization requires that TOP N return no more than N records (this is not the case for Visual FoxPro 8.0 and earlier versions) which is enforced if SET ENGINEBEHAVIOR is set to 90.

    TOP N PERCENT cannot be optimized unless the whole result set can be read into memory at once.

    Improved Optimization for Multiple Table OR Conditions

    Visual FoxPro 9.0 provides for improved Rushmore optimization involving multi-table OR conditions. Visual FoxPro uses multi-table OR conditions to Rushmore optimize filter conditions for a table as long as both sides of the condition can be optimized. The following example shows this:

     Copy Code
    CLEAR CREATE CURSOR Test1 (f1 I) FOR i=1 TO 20   INSERT INTO Test1 VALUES (I) NEXT INDEX ON f1 TAG f1 CREATE CURSOR Test2 (f2 I) FOR i=1 TO 20   INSERT INTO Test2 VALUES (I) NEXT INDEX ON f2 TAG f2 SYS(3054,12) SELECT * from Test1, Test2 WHERE (f1 IN (1,2,3) AND f2 IN (17,18,19)) OR ;   (f2 IN (1,2,3) AND f1 IN (17,18,19)) INTO CURSOR Result SYS(3054,0)

    In this scenario, table Test1 can be Rushmore optimized using the following condition:

    (f1 IN (1,2,3)  OR f1 IN (17,18,19))and table Test2 with the following:

    (f2 IN (17,18,19) OR f2 IN (1,2,3))

    Support for Local Buffered Data

    At times it can be beneficial to use SELECT – SQL to select records from a local buffered cursor in which the table has not been updated. Many times when creating controls like grids, list boxes, and combo boxes it is necessary to consider newly added records which have not yet been committed to disk. Currently, SQL statements are based on content that is already committed to disk.

    Visual FoxPro 9.0 provides language enhancements that allow you to specify if the data returned by a SELECT – SQL command is based on buffered data or data written directly to disk.

    The SELECT – SQL command now supports a WITH … BUFFERING clause that lets you specify if retrieved data is based on buffered data or data written directly to disk. For more information, see SELECT – SQL Command – WITH Clause.

    If you do not include the BUFFERING clause, the retrieved data is then determined by the setting for SET SQLBUFFERING command. For more information, see the SET SQLBUFFERING Command.

    Enhancements to other SQL Commands

    The following sections describe enhancements made to the INSERT – SQL Command, UPDATE – SQL Command, and DELETE – SQL Command commands in Visual FoxPro 9.0.

    UNION Clause in the INSERT – SQL Command

    In Visual FoxPro 9.0, a UNION clause is now supported in the INSERT – SQL Command.

    The following is the general syntax for the UNION clause.

    INSERT INTO … SELECT … FROM … [UNION SELECT … [UNION …]]

    Example

    The following example demonstrates the use of a UNION clause in INSERT-SQL.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor (field1 I,field2 I) CREATE CURSOR MyCursor1 (field1 I,field2 I) CREATE CURSOR MyCursor2 (field1 I,field2 I) INSERT INTO MyCursor1 VALUES (1,1) INSERT INTO MyCursor2 VALUES (2,2) INSERT INTO MyCursor SELECT * FROM MyCursor1 UNION SELECT * FROM MyCursor2 SELECT MyCursor LIST

    Correlated UPDATE – SQL Commands

    Visual FoxPro 9.0 now supports correlated updates with the UPDATE – SQL Command.

    If a FROM clause is included in the UPDATE -SQL command, then the name after UPDATE keyword defines the target for the update operation. This name can be a table name, an alias, or a file name. The following logic is used to select the target table:

    • If the name matches an implicit or explicit alias for a table in the FROM clause, then the table is used as a target for the update operation.
    • If the name matches the alias for the cursor in the current data session, then the cursor is used as a target.
    • A table or file with the same name is used as a target.

    The UPDATE -SQL command FROM clause has the same syntax as the FROM clause in the SELECT – SQL command with the following limitations:

    • The target table or cursor cannot be involved in an OUTER JOIN as a secondary table.
    • The target cursor cannot be a subquery result.
    • All other JOINs can be evaluated before joining the target table.

    The following is the general syntax for a correlated UPDATE command.

    UPDATE … SET … FROM … WHERE …

    Example

    The following example demonstrates a correlated update using the UPDATE -SQL command.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor1 (field1 I , field2 I NULL,field3 I NULL) INSERT INTO MyCursor1 VALUES (1,1,0) INSERT INTO MyCursor1 VALUES (2,2,0) INSERT INTO MyCursor1 VALUES (5,5,0) INSERT INTO MyCursor1 VALUES (6,6,0) INSERT INTO MyCursor1 VALUES (7,7,0) INSERT INTO MyCursor1 VALUES (8,8,0) INSERT INTO MyCursor1 VALUES (9,9,0) CREATE CURSOR MyCursor2 (field1 I , field2 I) INSERT INTO MyCursor2 VALUES (1,10) INSERT INTO MyCursor2 VALUES (2,20) INSERT INTO MyCursor2 VALUES (3,30) INSERT INTO MyCursor2 VALUES (4,40) INSERT INTO MyCursor2 VALUES (5,50) INSERT INTO MyCursor2 VALUES (6,60) INSERT INTO MyCursor2 VALUES (7,70) INSERT INTO MyCursor2 VALUES (8,80) CREATE CURSOR MyCursor3 (field1 I , field2 I) INSERT INTO MyCursor3 VALUES (6,600) INSERT INTO MyCursor3 VALUES (7,700) UPDATE MyCursor1 SET MyCursor1.field2=MyCursor2.field2, field3=MyCursor2.field2*10 FROM MyCursor2 ; WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 SELECT MyCursor1 LIST UPDATE MyCursor1 SET MyCursor1.field2=MyCursor3.field2 FROM MyCursor2, MyCursor3 ; WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 AND MyCursor2.field1=MyCursor3.field1 SELECT MyCursor1 LIST

    Correlated DELETE – SQL Commands

    Visual FoxPro 9.0 now supports correlated deletions with the DELETE – SQL Command.

    If a FROM clause has more than one table, the name after the DELETE keyword is required and it defines the target for the delete operation. This name can be a table name, an alias or a file name. The following logic is used to select the target table:

    • If the name matches an implicit or explicit alias for a table in the FROM clause, then the table is used as a target for the update operation.
    • If the name matches the alias for the cursor in the current data session, then the cursor is used as a target.
    • A table or file with the same name is used as a target.

    The DELETE -SQL command FROM clause has the same syntax as the FROM clause in the SELECT – SQL command with the following limitations:

    • The target table or cursor cannot be involved in an OUTER JOIN as a secondary table.
    • The target cursor cannot be a subquery result.
    • It should be possible to evaluate all other JOINs before joining the target table.

    The following is the general syntax for a correlated DELETE command.

    DELETE [alias] FROM alias1 [, alias2 … ] … WHERE …

    Example

    The following example demonstrates a correlated deletion using the DELETE -SQL command.

     Copy Code
    CLOSE DATABASES ALL CREATE CURSOR MyCursor1 (field1 I , field2 I NULL,field3 I NULL) INSERT INTO MyCursor1 VALUES (1,1,0) INSERT INTO MyCursor1 VALUES (2,2,0) INSERT INTO MyCursor1 VALUES (5,5,0) INSERT INTO MyCursor1 VALUES (6,6,0) INSERT INTO MyCursor1 VALUES (7,7,0) INSERT INTO MyCursor1 VALUES (8,8,0) INSERT INTO MyCursor1 VALUES (9,9,0) CREATE CURSOR MyCursor2 (field1 I , field2 I) INSERT INTO MyCursor2 VALUES (1,10) INSERT INTO MyCursor2 VALUES (2,20) INSERT INTO MyCursor2 VALUES (3,30) INSERT INTO MyCursor2 VALUES (4,40) INSERT INTO MyCursor2 VALUES (5,50) INSERT INTO MyCursor2 VALUES (6,60) INSERT INTO MyCursor2 VALUES (7,70) INSERT INTO MyCursor2 VALUES (8,80) CREATE CURSOR MyCursor3 (field1 I , field2 I) INSERT INTO MyCursor3 VALUES (6,600) INSERT INTO MyCursor3 VALUES (7,700) DELETE MyCursor1 FROM MyCursor2 ; WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 SELECT MyCursor1 LIST RECALL ALL DELETE MyCursor1 FROM MyCursor2, MyCursor3 ; WHERE MyCursor1.field1>5 AND MyCursor2.field1=MyCursor1.field1 AND MyCursor2.field1=MyCursor3.field1 SELECT MyCursor1 LIST RECALL ALL DELETE FROM MyCursor1 WHERE MyCursor1.field1>5 SELECT MyCursor1 list RECALL ALL DELETE MyCursor1 from MyCursor1 WHERE MyCursor1.field1>5 RECALL ALL IN MyCursor1 DELETE T1 ; FROM MyCursor1 T1 JOIN MyCursor2 ON T1.field1>5 AND MyCursor2.field1=T1.field1, MyCursor3 ; WHERE MyCursor2.field1=MyCursor3.field1 RECALL ALL IN MyCursor1

    Updatable Fields in UPDATE – SQL Command

    The number of fields that can be updated with the UPDATE – SQL Command is no longer limited to 128 as in prior versions of Visual FoxPro. You are now limited to 255, which is the number of fields available in a table.

    SET ENGINEBEHAVIOR

    The SET ENGINEBEHAVIOR Command has a new Visual FoxPro 9.0 option, 90, that affects SELECT – SQL command behavior for the TOP N clause and aggregate functions. For additional information, see the SET ENGINEBEHAVIOR Command.

    Data Type Conversion

    Conversion between data types (for example, conversion between memo and character fields) has been improved in Visual FoxPro 9.0. This conversion improvement applies to the ALTER TABLE – SQL Command with the COLUMN option as well as structural changes made with the Table Designer.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    ALTER TABLE – SQL Command

    SET SQLBUFFERING Command

    SET ENGINEBEHAVIOR Command

    Class Enhancements

    Visual FoxPro contains the following enhancements to classes, forms, controls and object-oriented related syntax.

    Anchoring Visual Controls

    You can anchor a visual control to one or more edges of its parent container using the control’s Anchor property. When you anchor a visual control to the parent container, the edges of the control remain in the same position relative to the edges of the container when you resize the container. For more information, see Anchor Property.

    Docking Forms

    Visual FoxPro extends docking support to user-defined forms. Docking forms works similarly to docking toolbars except that you can dock forms to Visual FoxPro Interactive Development Environment (IDE) windows and other forms, and controls on the form can still obtain focus when the form is docked. Visual FoxPro includes the following new and updated properties, methods, and events to support docking forms.

    For more information, see How to: Dock Forms.

    CheckBox and OptionButton Controls Support Wordwrapping

    The WordWrap property is now supported for CheckBox and OptionButton controls. The text portions of these controls now use wordwrapping. For more information, see WordWrap Property.

    CommandButton Controls Can Align Text with Pictures

    The Alignment property now applies to CommandButton controls when specifying an image for the Picture property and setting the PicturePosition property to a value other than the default. The Alignment property also contains new and revised settings for CommandButton, CheckBox, and OptionButton controls. For more information, see Alignment Property.

    CommandButton, OptionButton, and CheckBox Controls Can Hide Captions

    The PicturePostion property contains a new setting of 14 (No text) for CommandButton, OptionButton, and CheckBox controls. You can use this setting to hide the text portions of these controls without needing to set the Caption property to an empty string. This setting is particularly useful when you want to include a hotkey for a button with a graphic without displaying the Caption text. You must set the Style property to 1 (Graphical) for this new setting to apply. In addition, the PicturePosition property now applies to CheckBox and OptionButton controls when Style is set to 1 (Graphical).

    For more information, see PicturePosition Property.

    PictureMargin and PictureSpacing Properties Control Spacing and Margins on CommandButton, OptionButton, and CheckBox Controls

    You can better control positioning of images on CommandButton, OptionButton, and CheckBox controls with the new PictureMargin and PictureSpacing properties. The PictureMargin property specifies margin spacing in pixels between an image and the control’s border as determined by the PicturePosition property. The PictureSpacing property specifies margin spacing in pixels between an image and text on the control.

    For more information, see PictureMargin Property and PictureSpacing Property.

    Collection Objects Support in ComboBox and ListBox Controls

    You can now specify Collection objects as the row source and row source type for the RowSource and RowSourceType properties of ComboBox and ListBox controls. For more information, see RowSource Property and RowSourceType Property.

    Setting Ascending or Descending Indexes on Cursors in the DataEnvironment

    You can specify ascending or descending order for a cursor index by using the new OrderDirection property for Cursor objects. Note: OrderDirection is disregarded when the cursor’s Order property is empty.

    For more information, see OrderDirection Property.

    Grid Supports Rushmore Optimization

    The Grid Control can be set to support Rushmore optimization if the underlying data source contains indexes that support this.

    For more information, see Optimize Property.

    Mouse Pointer Control for Grid Columns and Column Headers

    The MousePointer and MouseIcon properties now apply to Column objects in a grid and Header objects in a column. For the MousePointer property, you can specify the new setting of 16 (Down Arrow) to reset the mouse pointer for a column header to the default down arrow.

    For more information, see MousePointer Property and MouseIcon Property.

    Rotating Label, Line, and Shape Controls

    You can use the new Rotation property to rotate Label controls. The Rotation property applies to Line and Shape controls when used with the new PolyPoints property. For more information, see Rotation Property (Visual FoxPro), PolyPoints Property, and Creating More Complex Shapes using the PolyPoints Property.

    Label Controls Can Display Themed Background

    For Label controls, you can set the Style property to Themed Background Only to show only themed background colors when Windows themes are turned on. The label background color is the same as the parent container for the label. For more information, see Style Property.

    ListBox Controls Can Hide Scroll Bars

    You can use the new AutoHideScrollBar property for ListBox controls to hide scroll bars when the list contains less than the number of items that can be visible in the list box. For more information, see AutoHideScrollBar Property.

    Toolbar Controls Can Display Horizontal Separator Objects

    For Separator objects, set the Style property to 1 to display a horizontal line or a vertical line, depending on how the toolbar appears. If the toolbar appears horizontally, the line displays vertically. If the toolbar appears vertically, the line displays horizontally. In versions prior to this release, setting Style to 1 displayed a vertical line only. Note: In versions prior to this release, undocked vertical system and user-defined toolbars did not display horizontal separators. In the current release, horizontal separators now display for vertical undocked toolbars.

    For more information, see Style Property.

    Toolbar Controls Can Hide Separator Objects

    The Visible property now applies to Separator objects so you can control whether a Separator object displays in Toolbar controls. When used in combination with the Style property, the separator’s Visible property determines whether a space or line displays as the separator when its Style property is set to 0 (Normal – do not display a line) or 1 (display a horizontal or vertical line), respectively.

    For more information, see Visible Property (Visual FoxPro).

    Creating More Complex Shapes

    You can use the new PolyPoints property for Line and Shape controls to create polygon lines and shapes. PolyPoints specifies an array of any dimension containing coordinates with the format of X1, Y1, X2, Y2, …, organized in the order in which the polygon line or shape is drawn. For Line controls, when you create a polygon line using the PolyPoints property, you can specify the new setting of “S” or “s” for the LineSlant property to create a Bezier curve.

    For more information, see PolyPoints Property and LineSlant Property.

    ComboBox Controls Can Hide Drop-Down Lists

    You can now use the NODEFAULT command in the DropDown event for a ComboBox control. This will prevent the drop-down list portion of a ComboBox control from appearing. For more information, see NODEFAULT Command.

    NEWOBJECT( ) Creates Objects without Raising Initialization Code

    To mimic the behavior of a class opened in Class Designer or Form Designer, pass 0 to the cInApplication parameter. This feature allows you to create design-time tools that view the structure of a class. By passing 0 to the cInApplication parameter for the NEWOBJECT( ) function, Visual FoxPro allows you to create an instance of a class without raising initialization code (such as code in the Init, Load, Activate, and BeforeOpenTables events). Furthermore, when the object is released, it does not raise its destructor code (such as code in the Destroy and Unload events). Only initialization and destructor code is suppressed; code in other events and methods is still called. If you use the cInApplication parameter to suppress initialization and destructor code in an object, you also suppress it in the object’s child objects.

    This behavior is not supported for the NewObject Method.

    For more information, see NEWOBJECT( ) Function.

    Specify Where Focus is Assigned in the Valid Event

    To direct where focus is assigned, you can use the optional ObjectName parameter in the RETURN command of the Valid event. The object specified must be a valid Visual FoxPro object. If the specified object is disabled or cannot receive focus, then focus is assigned to the next object in the tab order. If an invalid object is specified, Visual FoxPro keeps the focus at the current object. You can now set focus to objects in the following scenarios: Set focus to an object on another visible form. Set focus to an object on a non-visible Page or Pageframe control.

    For more information, see Valid Event.

    TextBox Controls Have Auto-Completion Functionality

    You can add auto-completion functionality to your text box controls to make data entry more efficient. Auto-completion is the automatic display of a drop-down list of entries that match the string as it is typed into the text box. The entries come from a special table that tracks unique values entered into the text box, the control name that is the source of the value, and usage information. The following properties support auto-completion:

    By the setting the AutoComplete property, you determine the sort order for the entries. If you want more control over the list and where it is stored, you can use the AutoCompSource property to specify the table used to populate the automatic list. By default, the table is AUTOCOMP.DBF. You can use one table for each text box control or a single table can populate automatic lists for several text boxes.

    If you use a single table, which is the default, the table uses values in the Source field for each entry to identify the text box control associated with the entry. By default, the Source field value is the name of the text box control. You can specify the Source field value using the AutoCompSource property of the text box. For example, you might want to make the same set of entries available to multiple Text box controls within the application such as address information. You can explicitly set the AutoCompTable and AutoCompSource properties for each of the controls to the same table and source field value. The same automatic list appears for each of them.

    The text box control handles updating the auto-completion table for you based on the values actually entered in the text box. If you want to remove a value from the list, enter a string in a text box that matches the string you want to delete to filter the list for it. Select the entry in the list and press the DELETE key. The string remains in the table but no longer appears in the automatic list.

    Note:
    You can control the number of items that appear in the drop-down list using SYS(2910) – List Display Count.

    For more information, see AutoComplete Property, AutoCompSource Property, and AutoCompTable Property.

    New InputMask and Format Property Settings

    The following new InputMask and Format settings are available: InputMask Property cMask Description U Permits alphabetic characters only and converts them to uppercase (A – Z). W Permits alphabetic characters only and converts them to lowercase (a – z). Format Property cFunction Description Z Displays the value as blank if it is 0, except when the control has focus. Dates and DateTimes are also supported in these controls. The date and datetime delimiters are not displayed unless the control has focus.

    For more information, see InputMask Property and Format Property.

    Use PictureVal Property to Pass Images as Strings

    The Image control’s new PictureVal property can be used instead of the Picture Property (Visual FoxPro) to specify a character string expression or object of an image. For an object, the format must be of an IPicture interface format compatible with LOADPICTURE( ) Function.

    For more information, see PictureVal Property.

    CLEAR CLASSLIB Updated

    The CLEAR CLASSLIB command now automatically executes a CLEAR CLASS command on each class in the specified class library. Any errors that might occur during release of individual classes (e.g., class in use) are ignored. Note: Classes in other class libraries that are used or referenced by a class in the specified class library are not cleared.

    For more information, see CLEAR Commands.

    Screen Resolution Limit Increased

    In prior versions of Visual FoxPro, the definable maximum area for a form is limited to twice the Screen Resolution for both X and Y dimensions. For example, if your monitor resolution is 1280×1024, then the max dimensions would be:   Copy Code Form.Width = 2552 Form.Height = 2014 Additionally, if you attempted to set Width and Height properties to these limits in design-time and then ran the form, you would see that the values have reverted to screen resolution limits (being that they were saved this way):   Copy Code Form.Width = 1280 Form.Height = 998 In Visual FoxPro 9.0, this limitation has been increased to approximately 32,000 pixels for each dimension and now allows for more flexibility with certain forms such as scrollable ones:   Copy Code Form.Width = 32759 Form.Height = 32733

    For more information, see Width Property and Height Property.

    ProjectHook Source Code Control Events

    New events have been added to the ProjectHook class, which allow you to perform source code control operations such as check-in and check-out of multiple files at once.

    For more information, see SCCInit Event and SCCDestroy Event.

    AddProperty Method Supports Design Time Settings

    You can specify the visibility (Protected, Hidden or Public) and description of a property using the AddProperty method with new available parameters. These settings can also be controlled from the New Property Dialog Box and Edit Property/Method Dialog Box. For more information, see AddProperty Method.

    WriteMethod Method Supports Design Time Settings

    You can specify the visibility (Protected, Hidden or Public) and description of a method using the WriteMethod method with new available parameters. These settings can also be controlled from the New Property Dialog Box and Edit Property/Method Dialog Box. For more information, see WriteMethod Method.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    Language Enhancements

    In the current release of Visual FoxPro, you will find enhanced functionality via new and enhanced commands and functions. Expand and collapse this topic to see information about miscellaneous language additions and improvements.

    Class Enhancements

    Visual FoxPro contains significant language enhancements for classes, forms, controls, and object-oriented related features. For more information, see Class Enhancements.

    Data and XML Enhancements

    Visual FoxPro contains significant language enhancements for Data, SQL and XML features. For more information, see SQL Language Improvements and Data and XML Feature Enhancements.

    IDE Enhancements

    Visual FoxPro contains a number of language enhancements for features related to the IDE (Interactive Development Environment). For more information, see Interactive Development Environment (IDE) Enhancements and Enhancements to Visual FoxPro Designers.

    Printing and Reporting Enhancements

    Visual FoxPro contains a number of language enhancements to support new Reporting functionality:

    Additionally, there are improvements to the following related Printing language elements:

    • SYS(1037) – Page Setup Dialog Box Displays Visual FoxPro default or report Page Setup dialog box or sets printer settings for the default printer in Visual FoxPro or for the report printer environment. In this version, a new nValue parameter is available.
    • APRINTERS( ) Function Returns a five-column array with the name of the printer, connected port, driver, comment, and location. The last three columns are available if the new optional parameter is passed.
    • GETFONT( ) Function Contains an additional setting to display only those fonts available on the current default printer and clarified values for the language script.

    New Reporting functionality is described in more detail in separate Reporting topics. For more information, see Guide to Reporting Improvements.

    Specifying Arrays with More Than 65K Elements

    You can now specify arrays containing more than 65,000 elements, for example, when using the DIMENSION command. Normal arrays and member arrays have a new limit of 2GB. Arrays containing member objects retain a limit of 65,000 elements. Note: Array sizes can also be limited by available memory, which affects performance, especially for very large arrays. Make sure your computer has enough memory to accommodate the upper limits of your arrays.

    The Library Construction Kit, which contains the files Pro_Ext.h, WinAPIMS.lib, and OcxAPI.lib, still has a limit of 65,000 elements. For more information about these files, see Accessing the Visual FoxPro API, How to: Add Visual FoxPro API Calls, and How to: Build and Debug Libraries and ActiveX Controls. The SAVE TO command does not support saving arrays larger than 65,000 elements.

    For more information, see Visual FoxPro System Capacities and DIMENSION Command.

    STACKSIZE Setting Increases Nesting Levels to 64k

    For operations such as the DO command, you can change the default number of nesting levels from 128 levels to 32 and up to 64,000 levels of nesting by including the new STACKSIZE setting in a Visual FoxPro configuration file. Note: You can change the nesting level only during Visual FoxPro startup.

    For more information, see Special Terms for Configuration Files and Visual FoxPro System Capacities.

    Program and Procedure File Size Is Unrestricted

    In previous versions of Visual FoxPro, the size of a procedure or program could not exceed 65K. Visual FoxPro now removes this restriction for programs and procedures. For more information, see Visual FoxPro System Capacities.

    PROGCACHE Configuration File Setting

    In previous versions of Visual FoxPro, you could not specify the program cache size or amount of memory reserved to run programs. This configuration file setting allows you to control this. It is especially useful for MTDLL scenarios. For more information, see Special Terms for Configuration Files.

    ICASE( ) Function

    The new ICASE( ) function makes it possible for you to evaluate a list of conditions and return results depending on the result of evaluating those conditions. For more information, see ICASE( ) Function.

    TTOC( ) Converts DateTime Expressions to XML DateTime Format

    You can convert a DateTime expression into a character string in XML DateTime format by passing a new optional value of 3 to the TTOC( ) function. For more information, see TTOC( ) Function.

    SET COVERAGE Command Available at Run Time

    The SET COVERAGE command is now available at run time so that you can debug errors that occur at run time but not at design time. For more information, see SET COVERAGE Command.

    CLEAR ERROR Command

    The new ERROR clause for the CLEAR command makes it possible to reset the error structures as if no error occurred. This affects the following functions: The AERROR( ) function will return 0. The ERROR( ) function will return 0. The output from MESSAGE( ), MESSAGE(1) and SYS(2018) will return a blank string.

    The CLEAR command should not be used with the ERROR clause within a TRY…CATCH…FINALLY structure. For more information, see CLEAR Commands.

    Write Options Dialog Settings to Registry Using SYS(3056)

    The SYS(3056) function can now be used to write out settings from the Options dialog box to the registry. SYS(3056 [, nValue ]) The following table lists values for nValue. nValue Description 1 Update only from registry settings, with the exception of SET commands and file locations. 2 Write out settings to the registry.

    For more information, see SYS(3056) – Read Registry Settings.

    FOR EACH … ENDFOR Command Preserves Object Types

    Visual FoxPro now includes the FOXOBJECT keyword for the FOR EACH … ENDFOR command to support preservation of native Visual FoxPro object types. FOR EACH objectVar [AS Type [OF ClassLibrary ]] IN Group FOXOBJECT Commands [EXIT] [LOOP] ENDFOR | NEXT [Var] The FOXOBJECT keyword specifies that the objectVar parameter created will be a Visual FoxPro object. The FOXOBJECT keyword only applies to collections where the collection is based on a native Visual FoxPro Collection class. Collections that are COM-based will not support the FOXOBJECT keyword.

    For more information, see FOR EACH … ENDFOR Command.

    SET PATH Command Enhancements

    The SET PATH command now supports the ADDITIVE keyword. The ADDITIVE keyword appends the specified path to the end of the current SET PATH list. If the path already exists in the SET PATH list, Visual FoxPro does not add it or change the order of the list. Paths specified with the ADDITIVE keyword must be strings in quotes or valid expressions. In addition, the length of the SET PATH list has been increased to 4095 characters.

    For more information, see SET PATH Command.

    Trim Functions Control Which Characters Are Trimmed

    It is now possible to specify which characters are trimmed from an expression when using the TRIM( ), LTRIM( ), RTRIM( ), and ALLTRIM( ) functions. TRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, …]]]) LTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, …]]]) RTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, …]]]) ALLTRIM(cExpression[, nFlags] [, cParseChar [, cParseChar2 [, …]]]) You can specify that the trim is case-insensitive using the nFlag value of 0 bit and 1. The cParseChar parameter specifies one or more character strings to be trimmed from cExpression. A maximum of 23 strings can be specified in cParseChar. By default, if cParseChar is not specified, then leading and trailing spaces are trimmed from character strings or 0 bytes are removed for Varbinary data types. The cParseChar parameters are applied in the order they are entered. When a match is found, cExpression is truncated and the process repeats from the first cParseChar parameter.

    For more information, see the TRIM( ) Function, LTRIM( ) Function, RTRIM( ) Function, and ALLTRIM( ) Function topics.

    ALINES( ) Offers More Flexible Parsing Options

    The ALINES( ) function has been enhanced to provide several additional options such as case-insensitive parsing and improved handling of empty array elements. These options are available using the new nFlags parameter that replaces the older lTrim 3rd parameter. For more information, see ALINES( ) Function.

    Improvements in TEXT…ENDTEXT Statement

    You can use the TEXT…ENDTEXT command to eliminate line feeds using the new PRETEXT setting. A new FLAGS parameter controls additional output settings. For more information, see TEXT … ENDTEXT Command.

    Include Delimiters in STREXTRACT( ) Results

    The STREXTRACT( ) function has a new nFlags setting that allows you to include the specified delimiters with the returned expression. For more information, see STREXTRACT( ) Function.

    STRCONV( ) Enhanced to Allow for Code Page and FontCharSet

    For certain conversion settings, you can specify an optional Code Page or Fontcharset setting for use in the conversion. For more information, see STRCONV( ) Function.

    TYPE( ) Determines if an Expression is an Array

    The TYPE( ) function accepts the parameter, 1, to evaluate an expression to determine if it is an array. Type(cExpression, 1) The following character values are returned if the 1 parameter is specified. Return Value Description A cExpression is an array. U cExpression is not an array. C cExpression is a collection. cExpression must be passed as a character string.

    For more information, see TYPE( ) Function.

    BINTOC( ) and CTOBIN( ) Have Additional Conversion Capabilities

    The BINTOC( ) and CTOBIN( ) functions have update or new parameters that provide you with more control over the output of these functions. Additionally, these enhancements offer some improvements for working with Win32 API routines. For more information, see BINTOC( ) Function and CTOBIN( ) Function.

    MROW( ) and MCOL( ) Can Detect the Position of the Mouse Pointer

    The MROW( ) and MCOL( ) functions now have a zero (0) parameter for detecting the position of the mouse pointer based on the currently active form instead of the form returned by the WOUTPUT( ) function. Although they are typically reference the same form, if the AllowOutput property of the form is set to False (.F.), WOUTPUT( ) does not return the current active form. The mismatch of references and can lead to unexpected results. By using the zero (0) parameter, you can avoid misplacing items, such as Shortcut menus, as the currently active form is always used.

    For more information, see MROW( ) Function and MCOL( ) Function.

    INPUTBOX( ) Returns A Cancel Operation

    The INPUTBOX( ) function contains an additional parameter that allows you to determine if the user canceled out of the dialog. For more information, see INPUTBOX( ) Function.

    AGETCLASS( ) Supported for Runtime Applications

    The AGETCLASS( ) fiunction is now supported for runtime applications. For more information, see AGETCLASS( ) Function.

    SYS(2019) Extends Handling of Configuration Files

    You can use SYS(2019) to obtain the name and location of both internal and external configuration files. For more information, see SYS(2019) – Configuration File Name and Location.

    SYS(2910) Controls List Display Count

    You can control the number of items that appear in a drop-down list such as the one used by AutoComplete Property. This is the setting that is available on the View Tab, Options Dialog Box of the Options Dialog Box (Visual FoxPro).

    For more information, see SYS(2910) – List Display Count.

    SYS(3008) Turns Off Hyperlink Tip

    Visual FoxPro will display a tip such as “CTRL+Click to follow the link” when you hover over a hyperlink in the editor. If you desire to not have this tip show, you can use SYS(3008) to turn it off. This function is also useful for international applications where you do not want to display the English text for this tip. For more information, see SYS(3008) – Hyperlink Tooltips.

    SYS(3065) Internal Program Cache

    You can obtain the internal program cache (PROGCACHE configuration file setting). For more information, see SYS(3065) – Internal Program Cache.

    SYS(3101) COM Code Page Translation

    You can now specify a code page to use for character data translation involving COM interoperability. For more information, see SYS(3101) – COM Code Page Translation.

    Bidirectional Support for Tooltips and Popups

    For international applications that display text from right to left, you can use the following new enhancements to control text justification: SYS(3009) – right justifies text in ToolTips. DEFINE POPUP…RTLJUSTIFY – right justifies items in a popup, such as a shortcut menu. SET SYSMENU TO RTLJUSTIFY – right justifies an entire menu system.

    The SYS(3009) function is a global setting. For more information, see SYS(3009) – Bidirectional Text Justification for ToolTips, DEFINE POPUP Command and SET SYSMENU Command.

    Enhanced Font Script Support

    Visual FoxPro 9.0 contains a number of enhancements that extend ability to specify a Font Language Script (or FontCharSet) along with existing Font settings:

    • SYS(3007) – specifies a FontCharSet for ToolTips. This is a global setting.
    • FONT Clause – the following table lists commands that support an optional FONT clause that allows for specification of a FontCharSet in the following format: FONT cFontName [, nFontSize [, nFontCharSet]] Command DEFINE MENU DEFINE POPUP DEFINE BAR DEFINE PAD DEFINE WINDOW MODIFY WINDOW BROWSE/EDIT/CHANGE ?/??
    • Browse – the Font Dialog Box that you can invoke by selecting the Font menu item from the Table menu with a Browse Window active now allows for selection of a font language script. You can specify a global default font script from the IDE Tab, Options Dialog Box in the Options Dialog Box (Visual FoxPro). To do this, you must first check the Use font script checkbox.
    • Editors – the Font Dialog Box that you can invoke with an editor window active by selecting the Font menu item from the Format menu or right-click shortcut menu Edit Properties Dialog Box now allows for selection of a font language script. You can specify a global default font script from the IDE Tab, Options Dialog Box in the Options Dialog Box (Visual FoxPro). To do this, you must first check the Use font script checkbox.

    For more information, see SYS(3007) – ToolTipText Property Font Language Script, IDE Tab, Options Dialog Box, and FontCharSet Property.

    ToolTip Timeout Control

    You can specify how long a ToolTip is displayed if the mouse pointer is left stationary. For more information, see _TOOLTIPTIMEOUT System Variable.

    Tablet PC Features

    The following features are available to assist with applications designed to run on a Tablet PC computer. ISPEN( ) – determines if the last Visual FoxPro application mouse event on a Tablet PC was a pen tap. _SCREEN.DisplayOrientation – this read-write property specifies the screen display orientation for a Tablet PC. The value returned is the current orientation. _TOOLTIPTIMEOUT – specifies how long a ToolTip is displayed if the mouse pointer is left stationary.

    For more information, see ISPEN( ) Function, DisplayOrientation Property, and _TOOLTIPTIMEOUT System Variable.

    Windows Message Event Handling

    Visual FoxPro allows you to trap and handle window messages from the Microsoft® Windows® operating system using existing BINDEVENT functions. Some examples of common events you might wish to trap for include: A power broadcast message used to intercept standby or power-down activities. Media insertion and removal events, such as the insertion of a CD into a drive. The insertion and/or removal of a Plug and Play hard disk (e.g., USB Drive). Interception of screen saver queries to stop the screen saver from activating. Operating system level font changes and Windows XP Theme changes. New network connections/shares added or removed from system. Switching between applications. You can use the Visual FoxPro BINDEVENT functions to register (and unregister) event handlers used to intercept messages (i.e., Win32 API window messages that get processed by the Win32 WindowProc function). See MSDN for more details. The new BINDEVENT( ) syntax requires the hWnd (integer) of the window receiving the message you desire to intercept, and the specific message itself (integer). For example, power-management events such as standby and power-down use the Win32 WM_POWERBROADCAST message (value of 536). BINDEVENT(hWnd, nMessage, oEventHandler, cDelegate) The following example illustrates detection of a Windows XP Theme change:   Copy Code #DEFINE WM_THEMECHANGED 0x031A #DEFINE GWL_WNDPROC (-4) PUBLIC oHandler oHandler=CREATEOBJECT(“AppState”) BINDEVENT(_SCREEN.hWnd, WM_THEMECHANGED, oHandler, “HandleEvent”) MESSAGEBOX(“Test by changing Themes.”) DEFINE CLASS AppState AS Custom nOldProc=0 PROCEDURE Destroy UNBINDEVENT(_SCREEN.hWnd, WM_THEMECHANGED) ENDPROC PROCEDURE Init DECLARE integer GetWindowLong IN WIN32API ; integer hWnd, ; integer nIndex DECLARE integer CallWindowProc IN WIN32API ; integer lpPrevWndFunc, ; integer hWnd,integer Msg,; integer wParam,; integer lParam THIS.nOldProc=GetWindowLong(_VFP.HWnd,GWL_WNDPROC) ENDPROC PROCEDURE HandleEvent(hWnd as Integer, Msg as Integer, ; wParam as Integer, lParam as Integer) lResult=0 IF msg=WM_THEMECHANGED MESSAGEBOX(“Theme changed…”) ENDIF lResult=CallWindowProc(this.nOldProc,hWnd,msg,wParam,lParam) RETURN lResult ENDPROC ENDDEFINE The following SYS( ) functions are also available to assist with handing these events: SYS(2325) – returns the hWnd of a client window from the parent window’s WHANDLE. SYS(2326) – returns a Visual FoxPro WHANDLE from a window’s hWnd. SYS(2327) – returns a window’s hWnd from a Visual FoxPro window’s WHANDLE.

    For more information, see BINDEVENT( ) Function, UNBINDEVENTS( ) Function, and AEVENTS( ) Function. Also, see SYS(2325) – WCLIENTWINDOW from Visual FoxPro WHANDLE, SYS(2326) – WHANDLE from a Window’s hWnd, and SYS(2327) – Window’s hWnd from Visual FoxPro WHANDLE for related topics. Refer to MSDN as reference source for details on specific window messages.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    Interactive Development Environment (IDE) Enhancements

    To provide a more integrated development environment for your projects and applications, Visual FoxPro contains the following improved functionality for the IDE.

    Additional Project Manager Shortcut Menu Commands

    When docked, the Project Manager window contains the following additional shortcut menu commands that are available on the Project menu:

    • Close Closes the Project Manager.
    • Add Project to Source Control Creates a new source control project based on the current project. Available only when a source code control provider is installed and specified on the Projects tab in the Options dialog box.
    • Errors Displays the error (.err) file after running a build.
    • Refresh Refreshes the contents of the Project Manager.
    • Clean Up Project Removes deleted records from the Project Manager (.PJX) file.

    Modifying a Class Library from the Project Manager

    When you select a class library (.vcx) file in the Project Manager, you can now open and browse class libraries by clicking the Modify button. The class library opens in the Class Browser. For more information, see How to: Open Class Libraries.

    Set Font of Project Manager

    You can change the text font settings for the Project Manager window. Right-click the Project Manager window (outside of the tree hierarchy window) and choose Font.

    Generating Message Logs During Project Build and Compile

    When you build a project, application, or dynamic-link library, Visual FoxPro automatically generates an error (.err) file that includes any error messages, if they exist, when the build process completes. When you select the Display Errors check box in the Build Options dialog box, Visual FoxPro displays the .err file when the build completes. Selecting the Recompile All Files check box includes compile errors in the .err file. Build status messages usually appear in the status bar. However, in previous versions, if the build process is interrupted, Visual FoxPro did not write the .err file to disk. In the current release, Visual FoxPro writes build status and error messages to the .err file as they occur during the build process. If the build process is interrupted, you can open the .err file opens to review the errors. Note: If no errors occur during the build, the .err file is deleted. If the Debug Output window is open, build status and error messages appear in the window. You can save messages from the Debug Output window to a file.

    For more information, see How to: View and Save Build Messages.

    Properties Window Enhancements

    • Design time support for entering property values greater than 255 characters and extended characters, such as CHR(13) (carriage return) and CHR(10) (linefeed), has been added to visual class library (.vcx) and form (.scx) files. You can now enter up to 8k characters in length. Note: Extended property value support is only available through the Properties Window (Zoom dialog box) for custom user-specified properties as well as certain native ones such as CursorSchema and Value. For properties not supported, you can still specify values which are longer than 255 characters, or contain carriage returns and linefeeds by assigning them in code such as during the object’s Init Event. The Zoom dialog box and Expression Builder dialog box have been updated to support this. The Properties window includes a Zoom (Z) button that appears next to the property settings box for appropriate properties. Caution: Property values that exceed 255 characters or include carriage return and/or linefeed characters are stored in a new format inside the .vcx or .scx file. If you attempt to modify these classes in a prior version, an error occurs. This feature is particularly useful for setting the CursorAdapter CursorSchema property to any schema expression when schemas might exceed 255 characters.
    • The Properties window font can now be specified by the new Font shortcut menu option. This new menu replaces the Small, Medium and Large font menu items used in prior versions. This font is also used in the description pane, and object and property value dropdowns. Note: Bold and italic font styles are reserved for non-default property values and read-only properties, respectively. If a bold or italic font style is chosen, then the Properties window inverts the displayed behavior. For example, if one chooses an italic font style, read-only properties appear in normal font style and all others in italic.
    • Colors can be specified for certain types of properties by right clicking on the Properties Window and selecting following menu items:Note: If a conflict exists between color settings, the Instance setting takes priority followed by the Non-Default one.

    For more information, see Zoom <property> Dialog Box, Expression Builder Dialog Box, CursorSchema Property, and Properties Window (Visual FoxPro).

    MemberData Extensibility

    The MemberData extensibility architecture lets you provide metadata for class members (properties, methods and events). With MemberData, you can specify a custom property editor, display a property on the Favorites tab, or change the capitalization in the Properties Window (Visual FoxPro).

    For more information, see MemberData Extensibility.

    Setting Default Values for New Properties

    When adding a new property to a class, you can specify an initial value other than the default in the New Property dialog box. Subclasses inherit these default values unless you reset the default values to the parent class. In previous versions, you had to set the default value for the new property by selecting the property in the Properties window and setting the default value.

    For more information, see How to: Add Properties to Classes.

    Document View Sort Options

    You can now sort items in the Document View window by name for forms and visual class libraries.

    See Document View Window for more information on sorting items in the Document View Window.

    Compiling Code in the Background

    Visual FoxPro performs background compilation when syntax coloring is turned on in the Command window and Visual FoxPro editors for program (.prg) files, methods, stored procedures, and memos. The Expression box in the Expression Builder dialog box also includes support for background compilation and syntax coloring when turned on. When the single and current line of code that you are typing contains invalid syntax, Visual FoxPro displays the line of code with the formatting style selected in the Editor tab of the Options dialog box. Note: Syntax coloring must be turned on for background compilation to function. Background compilation does not detect invalid syntax in multiple lines of code, including those containing continuation characters.

    For more information, see How to: Display and Print Source Code in Color.

    Rich Text Format (RTF) Clipboard Support

    Visual FoxPro now supports copying in RTF (Rich Text Format) to the clipboard. Visual FoxPro preserves the style (bold, italic, and underline) and color attributes.

    RTF is supported only in the FoxPro editors that allow for syntax coloring, such as the Command window and editing windows opened with MODIFY COMMAND Command. The RTF clipboard format is only supported when syntax coloring is enabled such as from Edit Properties Dialog Box. You can disable RTF clipboard format with the _VFP EditorOptions Property.

    The _CLIPTEXT System Variable does not support RTF.

    Find Dialog Box Improvements

    The following improvements were made to Find support:

    • If a word is selected in a Visual FoxPro editor, the Find Dialog Box (Visual FoxPro) when opened now displays the word in the Look For drop-down box. If Find has not yet been used for a running instance of Visual FoxPro, a word positioned under the insertion pointer will appear in the Look For drop-down. If multiple words are selected, only the first word appears in the drop-down (use copy and paste to enter multiple words).
    • When a Browse window is open and you search for a word with the Find dialog box, you can search for the word again (Find Again) after the Find dialog box is closed by pressing the F3 key.
    • You can now use Find to search for content in Name column of the Watch and Locals debug windows (see Debugger Window). When searching object members, Find searches in these debug windows are limited to nodes that have been expanded and one level below.

    View Constants in Trace Window

    Constants (#DEFINE values) can be viewed in the Trace Window when you hover over it with the mouse.

    Note:
    Visual FoxPro evaluates constants as expressions in the Trace Window and may have difficulty interpreting a specific #DEFINE when you hover over it with the mouse. Consequently, if there are multiple expressions on a line, they are all displayed in the value tip.

    Printing Selected Text in Editor Windows

    You can print selected text from Visual FoxPro editor windows. When you have text selected in the editor window, the Selection option in the Print dialog box is available and selected. Note: If a partial line is selected, the entire line is printed.

    For more information, see Print Dialog Box (Visual FoxPro).

    System Font Improvements

    To improve legibility on high-resolution monitors, Error dialog boxes and the Zoom <property> Dialog Box in the Properties window now use the Windows Message Box text font.

    In Windows XP, the Windows Message Box text font is set by opening Display in the Control Panel, and then clicking Advanced on the Appearance tab.

    IntelliSense Saves Settings Between User Sessions

    Visual FoxPro now saves IntelliSense settings, such as turning IntelliSense on, between user sessions. These settings are controlled by the _VFP EditorOptions property. In addition, the settings in the _VFP EditorOptions property are saved in the FoxUser.dbf resource file. For more information, see EditorOptions Property.

    IntelliSense in Memo Field Editor Window

    Visual FoxPro includes IntelliSense support in Memo field editor windows when syntax coloring is turned on.

    IntelliSense Available for Runtime Applications

    Selected IntelliSense features are available at run time in distributed Visual FoxPro 9.0 applications. In order to use IntelliSense at run time, you need to set the _FOXCODE and _CODESENSE variables, and EditorOptions Property. Note: With runtime applications, syntax coloring does not need to be turned on for an editor to support IntelliSense.

    For more information, see IntelliSense Support in Visual FoxPro, _FOXCODE System Variable, _CODESENSE System Variable and EditorOptions Property.

    IntelliSense Support in WITH … ENDWITH and FOR EACH … ENDFOR Commands

    Visual FoxPro now supports IntelliSense within the WITH … ENDWITH Command and FOR EACH … ENDFOR Command.

    WITH ObjectName [AS Type [OF ClassLibrary]]

      Commands

    ENDWITH

    FOR EACH ObjectName [AS Type [OF ClassLibrary]] IN Group

      Commands

      [EXIT]

      [LOOP]

    ENDFOR

    The Type parameter can be any valid type, including data types, class types, or ProgID. If the class name cannot be found, Visual FoxPro disregards Type and does not display IntelliSense for it.

    Note:
    The type reference does not affect the functionality of the application at run time. The type reference is only used for IntelliSense.

    The ObjectName expression can refer to a memory variable or an array.

    The ClassLibrary parameter must be in a path list that is visible to Visual FoxPro. You must specify a valid class library; references to existing objects are not valid. If Visual FoxPro cannot find the specified class library, IntelliSense does not display.

    Types expressed as ProgIDs and class libraries do not require quotation marks (“”) to enclose them unless their names contain spaces.

    When a user types the AS keyword, IntelliSense displays a list of types registered in the FoxCode.dbf table with Type “T”. If you have specified a valid type, typing a period within a WITH … ENDWITH or a FOR EACH … ENDFOR command displays IntelliSense for that object reference.

    Visual FoxPro supports IntelliSense for nested WITH … ENDWITH and FOR EACH … ENDFOR commands. The following is an example of nested WITH … ENDWITH commands in a class defined in a program (.prg) file named Program1.prg. To use, paste this code into a new program named Program1.prg, save it and then type a period (.) inside the WITH … ENDWITH block.

     Copy Code
    DEFINE CLASS f1 AS form MyVar1 = 123 ADD OBJECT t1 AS mytext PROCEDURE Init   WITH THIS AS f1 OF program1.prg     WITH .t1 AS mytext OF program1.prg     ENDWITH   ENDWITH ENDPROC ENDDEFINE DEFINE CLASS mytext as textbox MyVar2 = 123 ENDDEFINE

    IntelliSense provides limited List Values functionality for selected properties that begin with a “T” or “F” within a WITH … ENDWITH or FOR EACH … ENDFOR command. This is done to avoid possible conflicts with the common property values True (.T.) and False (.F.). If you just type “.T” or “.F” and press Enter, the word selected in the List Value drop-down does not expand. You need to type at least two letters for IntelliSense to insert the selected word.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    Enhancements to Visual FoxPro Designers

    You might want to open the following designers and find the enhancements.

    Report and Label Designers

    You can use the Report Builder available in the Report Designer and Label Designer to perform reporting tasks, configure settings, and set properties for reporting features such as report layout, report bands, data groups, report controls, and report variables. For example, you can perform the following tasks: Prevent users from modifying reports, report controls, and report bands when editing the report in protected mode. Display captions instead of expressions for Field controls at design time. Display user-defined ToolTips for report controls. Set the language script for reports. Save the report data environment as a class.

    By default, the Report Builder activates when you interact with the Report and Label designers. However, you can use the _REPORTBUILDER system variable to specify ReportBuilder.app. The Report Builder consolidates, replaces, and adds to the functionality found in previous Report Designer user interface elements, which remain in the product and are available by setting _REPORTBUILDER. You can write custom report builders to extend reporting functionality and output or run reports with report objects. For more information, see Working with Reports and _REPORTBUILDER System Variable.

    Menu Designer

    You can set the _MENUDESIGNER system variable to call your own custom designer for creating menus.   Copy Code _MENUDESIGNER = cProgramName

    For more information, see _MENUDESIGNER System Variable.

    Table Designer

    The Table Designer accommodates the following data enhancements:

    Query and View Designers

    You can use spaces in table names specified in SQL statements in the Query and View designers if you provide an alias. For example, editing the following statement is valid in the View and Query designers:   Copy Code SELECT * from dbo.”Order Details” Order_Details

    For more information, see SELECT – SQL Command.

    Data Environment Designer

    The full path to the database (DBC) appears in the status bar when you select a database in the Add Table or View Dialog Box.

    Class and Form Designers

    The name of the class you are modifying appears in the title bar for the following dialog boxes:

    The View menu for the Form Designer offers both options for specifying the tab order on forms: Assign Interactively or Assign by List.

    In the Class, Form, and Report designers, you can use the following keyboard shortcut commands to adjust spacing between selected items.

    ShortcutDescription
    ALT+Arrow KeyAdjusts the spacing between the selected objects by one pixel in the direction of the arrow key.
    ALT+CTRL+Arrow KeyAdjusts the spacing between the selected objects by one grid scale in the direction of the arrow key.

    For more information, see Interactive Development Environment (IDE) Enhancements.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Miscellaneous Enhancements

    Changes in Functionality for the Current Release

    Miscellaneous Enhancements

    Visual FoxPro contains the following miscellaneous enhancements. You can click Collapse All to view a list of enhancments.

    Printing Dialog Boxes and Printing Language Enhancements

    Visual FoxPro includes various enhancements for its printing dialog boxes and printing language. Visual FoxPro uses the latest operating system dialogs for Printer Setup and other related printing operations. If the user is running on Windows XP, the dialogs will appear Themed. The following language functions contain new enhancements that impact general printing operations:

    For more information, see Language Enhancements.

    Improved Support for Applications Detecting Terminal Servers

    Visual FoxPro now automatically includes support for applications that are generated by the build process to detect whether they are running on a Terminal Server and prevent loading of unnecessary dynamic-link library (.dll) files that can impact performance. For more information, see BUILD EXE Command.

    Updated Dr. Watson Error Reporting to 2.0

    Visual FoxPro includes and updates its product error reporting to support Dr. Watson 2.0. This version includes new and improved error reporting, logging, and auditing features. For example, errors are logged while offline and are posted when you reconnect.

    Anchor Editor Application

    Visual FoxPro 9.0 allows you to create a custom property editor through extended metadata attributes for class members. Through this new extensibility model, you now have the ability to extend the functionality of class properties and methods, allowing you to create design-time enhancements such as a custom property editor. For more information about creating custom property editors, see MemberData Extensibility.

    A sample custom property editor, Anchoreditor.app, is included in Visual FoxPro 9.0 and is located in the Wizards directory. This application is run when the Anchor property is double-clicked in the Properties window, or by choosing the Anchor property in the Properties window and clicking the ellipsis button (…).

    TermDefinition
    Anchor but do not resize verticallySpecifies that the center of the control is anchored to the top and bottom edges of its container but the control does not resize.
    Anchor but do not resize horizontallySpecifies that the center of the control is anchored to the left and right edges of its container but the control does not resize.
    Border valuesDisplays the current settings for the border values.
    Common settingsSelects commonly used settings for the Anchor property.
    SampleClick the Sample button to test the current anchor value on a sample form.
    Anchor valueThe Anchor property value that is the combination of the current settings for the border values.

    Class Browser

    You can open and view class definitions that are specified within a program (.prg) similarly to class libraries (.vcx). You can select a program (.prg) from the File  Open/Add dialog box. See Class Browser Window for more information.

    CursorAdapter Builder

    The CursorAdapter Builder contains a number of enhancements that correspond to improvements added to the CursorAdapter class. See CursorAdapter Builder for more information.

    Toolbox

    The Toolbox (Visual FoxPro) is now dockable and can be docked to the desktop or other IDE windows.

    Code References

    The Code References Window has been updated with the following minor enhancements:

    • For the results grid, the Options dialog provides a new setting to show separate columns for class, method, and line, rather than concatenating them all in a single column. 
    • You can now sort by method name by right-clicking on the method header or selecting the Sort By menu item from the right-click menu.
    • With the results tree list, the following new right-click menu options are available:
      • Expand All – expands all nodes
      • Collapse All – collapses all nodes
      • Sort by Most Recent First – puts the most recent result sets at the top of the list rather than at the bottom
    Note:
    The results beneath a tree node are not filled until the node is expanded.  This is done to increase performance if you have a large result sets.

    GENDBC.PRG

    The Gendbc.prg program which generates program used to recreate a database has been updated with following minor enhancements:

    • Support for new Varchar, Varbinary and Blob field types
    • Support for AllowSimultaneousFetch, RuleExpression, and RuleText properties for views

    Environment Manager Task Pane

    The Environment Manager Task Pane has been enhanced with the following features:

    Data Explorer Task Pane

    The Task Pane Manager includes the new Data Explorer Task Pane which allows you to view and work with remote data sources such as SQL Server databases.

    For more information, see Data Explorer Task Pane.

    MemberData Editor

    The new MemberData Editor lets you edit MemberData for your classes. The MemberData Editor is available from the Class menu when the Class Designer is active. The MemberData Editor is also invoked silently when you right-click on an item in the Properties Window and select the Add to Favorites menu item. The MemberData Editor application is specified as a builder and can be changed in the Builder.dbf table located in your Wizards directory.

    For more information, see MemberData Editor and MemberData Extensibility.

    New Foundation Classes (FFC)

    The following are new FoxPro Foundation classes added to this version of Visual FoxPro:

    • _REPORTLISTENER.VCX – a set of core classes you can use when creating custom report listeners.
    • _FRXCURSOR.VCX – a class library used for working with report (FRX) files.
    • _GDIPLUS.VCX – a set of classes you can use for GDI+ handling. This is intended primarily for use when creating custom report listener classes.

    New Solution Samples

    Visual FoxPro 9.0 contains many new samples that show off new features in the product. To see a list of these samples, select the Solution Samples task pane in the Task Pane Manager and expand the New in Visual FoxPro 9.0 node.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Changes in Functionality for the Current Release

    Changes in Functionality for the Current Release

    Visual FoxPro includes functionality that differs from previous versions and might affect existing code. These behavior changes are organized according to the following categories:

    • Critical Changes   Functionality changes most likely to affect existing code when running under this version of Visual FoxPro. It is extremely important that you read this section.
    • Important Changes   Functionality changes that might affect existing code when running under this version of Visual FoxPro.
    • Miscellaneous Changes   Functionality changes you should know about but are not likely to impact existing code.
    • Removed Items   Features or files that existed in prior versions of Visual FoxPro but are no longer included.

    Critical Changes

    Critical behavior changes will most likely to affect existing code when running under this version of Visual FoxPro. SQL SELECT IN (Value_Set) Clause

    In previous versions of Visual FoxPro, the IN (Value_Set) clause for the WHERE clause in the SQL SELECT command is mapped to INLIST( ) function. In the current release, Visual FoxPro might stop evaluating values and expressions in the Value_Set list when the first match is found. Therefore, if the IN clause is not Rushmore-optimized, you can improve performance by placing values most likely to match in the beginning of the Value_Set list. For more information, see the description for the IN clause in the SELECT – SQL Command topic and the INLIST( ) Function.

    Conversion of INLIST( ) Function in the Query Designer and View Designer

    In previous versions of Visual FoxPro, the Query Designer and View Designer convert INLIST( ) function calls in the WHERE clause of the SQL SELECT command into IN (Value_Set) clauses. In the current release, this conversion no longer occurs due to the differences between INLIST( ) and the SQL IN clause. INLIST( ) remains restricted to 24 arguments. For more information, see the description for the IN clause in the SELECT – SQL Command topic and the INLIST( ) Function.

    Grids and RecordSource and ControlSource Properties

    In Visual FoxPro 9.0 there is a change in Grid control behavior. When the RecordSource property for a Grid control is set, Visual FoxPro 9.0 resets all ControlSource properties to the empty string (“”) for all columns. In earlier versions of Visual FoxPro, the ControlSource properties were not properly reset, so problems could occur when a RecordSource with a different structure was later bound. This change may impact scenarios involving Access and Assign methods or BINDEVENT( ) function calls made against a Grid column’s ControlSource property.

    Important Changes

    Important changes might affect existing code when running under Visual FoxPro 9.0. Reporting

    Visual FoxPro contains many improvements for reporting. The following are behavior changes that could impact existing reports:

    • The Report Designer and Engine now make use of extensible components. You can control or eliminate use of design-time extensions by altering the value of _REPORTBUILDER System Variable. You control run-time extension use with the SET REPORTBEHAVIOR Command.
    • In Visual FoxPro 9’s new object-assisted reporting mode, report fields may need to be adjusted (widened) slightly. This is especially important for numeric data where a field that is not wide enough to display the entire number will show it instead as asterisks (*****). For more information about the changes to the Report System that required this change, and features of the GDI+ rendering engine and other changes related to it, see Using GDI+ in Reports. For migration strategy and recommendations, see Guide to Reporting Improvements.
    • For a table of additional, minor rendering differences between backward-compatible reporting mode and object-assisted reporting mode, see the table below. Rendering feature Behavior in backward-compatible mode Behavior in object-assisted mode Recommendations Tab stops (CHR(9) values included in report data) The width of a tab stop is determined by a number of characters in the font used. Tab stops are set at fixed-width positions, regardless of font. If you concatenated tabs with data in a stretching report layout element to create a table format within the element, you can often fulfill the same requirements using a second detail band in Visual FoxPro 9. Alternatively, change the number of tabs you concatenate with your data. Special characters and word-wrapping Non-breaking spaces are not respected; they are treated as normal space characters. Special characters such as non-breaking spaces (CHR(160)) and soft hyphens (CHR(173)) are correctly interpreted. As a result, words may wrap differently in output. Evaluate the results. In most cases, users will appreciate the change, because it more faithfully representing their original intentions in the text. If necessary, use the CHRTRAN( ) Function or STRTRAN( ) Function to replace these special characters with standard spaces and hyphens. Line spacing of multi-line objects Line spacing is determined by a formula that does not take font properties into consideration. Lines in a multi-line object are individually rendered, so background colors for each line may appear to have a different width. GDI+ line spacing is dynamically determined using font characteristics. A multi-line object is rendered as a single block of text. Evaluate the results. In most cases, the change in line spaces will provide a more polished appearance, and in all cases this method of handling multi-line text provides better performance. If a report depends on the old style of spacing lines, you can adjust the ReportListener’s DynamicLineHeight Property to revert to the old behavior. Cursor images (.CUR files) .CUR files can be used as image sources in reports. .CUR files are not supported as image sources. Convert the cursor file to another, supported image format. Shape (Rounded Rectangle) curvature Limited choices for curvature. More choices are available through the Report Builder Application dialog box interface, but some will not look the same way in backward-compatible mode and object-assisted mode. If reports have to run in both backward-compatible mode and object-assisted mode, or if they are designed in version 9.0 but must run in earlier versions, limit your choices of values of shape curvature to those allowed in the native Round Rectangle Dialog Box. If you are using the Style Tab, Report Control Properties Dialog Box (Report Builder) to design such reports, use the values 12, 16, 24, 32, and 99, to represent the native buttons, selecting the buttons from left to right. The default value in the Round Rectangle dialog box (second button) is 16.
    • When you create a Quick Report, by using the CREATE REPORT – Quick Report Command or by invoking the Quick Report… option on the Report menu, and if you have SET REPORTBEHAVIOR 90, the layout elements created by the Report Designer are sized differently from ones created for the same fields in previous versions. This change handles the additional width required by the new rendering mechanism of the report engine.
    • If you use the KEYBOARD Command or PLAY MACRO Command statements to address options on the Report menu, you may need to revise the keystrokes in these statements, as the menu has been reorganized.
    • Reports may take longer to open in the Report Designer if the report was previously saved with the Printer Environment setting enabled. You can improve performance by unchecking the Printer Environment menu item from the Report menu and re-saving the report. The saved Printer Environment is not critical for functioning of a report and is typically not recommended. Object-assisted report mode also respects different printers’ resolution settings, so saving resolution information for one printer in your report may have adverse effects in an environment with printers that have different resolutions. A saved Printer Environment may also have more adverse affects on REPORT FORM or LABEL commands invoked with the TO FILE option than they did in previous versions, if the associated printer setup is not available in the environment at runtime. In Visual FoxPro 9, the global default for this setting in the Reports Tab, Options Dialog Box, and for reports created in executable applications (.exe files), has been changed to unchecked.
    • Because of changes to the way Visual FoxPro 9 uses current printer settings to determine items such as print resolution and page height dynamically, a REPORT FORM or LABEL command will not run in object-assisted mode if there are no available printer setups in the environment or if the print spooler has been stopped. You will receive Error loading printer driver (Error 1958). If you need to run reports in an environment with no printer information, perhaps creating custom types of output that do not require printers, you can supply Visual FoxPro with the minimal set of information it needs to run your report by supplying a page height and page width from the appropriate Report Listener methods. For more information, see GetPageHeight Method and GetPageWidth Method.
    • By default, and by design, the Report Builder Application does not automatically show tables in the report’s Data Environment when you build report expressions. To better protect end-user design sessions, only tables you have explicitly opened, not all tables from the DataEnvironment, are available in the Expression Builder. With this change, you have the opportunity to set up the design session’s data exactly the way you want the end-user to see it, before you issue a MODIFY REPORT Command in your application. If you prefer the Report Designer’s old behavior, you can change the Report Builder Application to emulate it. For more information, see How to: Replace the Report Builder’s Expression Builder Dialog Box.
    • The ASCII keyword on the REPORT FORM Command is documented as following the <filename> parameter of the TO FILE <filename> clause. In earlier versions of Visual FoxPro, you could safely use the incorrect and unsupported syntax TO FILE ASCII <filename> instead. This incorrect syntax triggers an error in Visual FoxPro 9. Note that the ASCII keyword has no effect on object-assisted mode output provided by the Report Engine, although a ReportListener Object can be written to implement it.
    • The keyword NOCONSOLE has no default meaning in object-assisted reporting mode, because ReportListeners do not echo their rendering output to the current output window by default. However, a ReportListener can mimic backward-compatible mode in this respect, if desired. Refer to OutputPage Method for a complete example.
    • To facilitate development of run-time reporting extensions, the Report Engine now allows normal debugging procedures during a report run. If your error handling routine assumes it is impossible for a report to be suspended, this assumption will now be challenged. Refer to Handling Errors During Report Runs for a detailed look at the associated changes, and some suggestions for strategy.
    • REPORT FORM and LABEL commands are no longer automatically prohibited as user-interface-related commands in COM objects compiled into DLLs, when you run the commands in object-assisted mode. The restriction still applies to these commands when they are run in backward-compatible mode. (The topic Selecting Process Types explains why user-interface-related commands are prohibited in DLLs.) This change is not applicable to multi-threaded DLLs. A number of user-interface-related facilities also are not available in DLLs (whether single- or multi-threaded). For example, the TXTWIDTH( ) Function and TextWidth Method depend on a window handle to function, so they are not available in a DLL. The CREATE REPORT – Quick Report Command relies on the same facilities as TXTWIDTH(), and therefore is not available in a DLL. However, in many instances, creating custom output using a ReportListener does not require any user-interface activity, so a REPORT FORM or LABEL command can now be used productively in a DLL. Using the SYS(2335) – Unattended Server Mode function to trap for potential modal states, as well as the new SET TABLEPROMPT Command, is recommended. Refer to Server Design Considerations and Limitations for more information.
    • Changes have occurred to the handling of group headers and footers in multi-column reports, when the columns flow from left to right (label-style layout). The revised behavior is more useful and behaves consistently with the new detail header and footer bands as well. For a description of the change, see How to: Define Columns in Reports.
    • In previous versions, the NOWAIT keyword on the REPORT FORM and LABEL commands was not significant when the commands were issued in the Command window rather than in a program. In Visual FoxPro 9’s object-assisted mode, when previewing instructions are interpreted by the Report Preview Application, this keyword is significant no matter where you issue the command. The Report Preview Application uses the NOWAIT keyword, consistently, as an instruction to provide a modeless preview form. For more information about the Report Preview Application, see Extending Report Preview Functionality.
    • Visual FoxPro 8 introduced the NOPAGEEJECT keyword on the REPORT FORM and LABEL commands, but applied the keyword only to printed output. In Visual FoxPro 9, NOPAGEEJECT has significance for all output targets, including PREVIEW. This keyword provides chained or continued report runs for multiple REPORT FORM and LABEL commands. To facilitate this behavior in preview mode, and to allow you to apply customization instructions to multiple previews, the Report Output Application caches a single ReportListener object instance for preview output, causing a change in behavior for multiple modeless report commands (REPORT FORM … PREVIEW NOWAIT). In the past, you used multiple REPORT FORM… PREVIEW NOWAIT statements in a sequence, your commands resulted in multiple report preview windows. In Visual FoxPro 9, when SET REPORTBEHAVIOR 90, these commands will result in successive report previews being directed to a single report preview window. Tip: You can easily invoke the old behavior by creating multiple ReportListener object references and associating one with each separate REPORT FORM or LABEL command, using the OBJECT keyword. For more information about using the OBJECT syntax, see REPORT FORM Command. For information about receiving multiple object references of the appropriate type from the Report Output Application, see Understanding the Report Output Application.
    • In the process of reviewing and overhauling the native Report Engine, a number of outstanding issues regarding band and layout element positioning were addressed. For example, a field element marked to stretch and sized to take up more than one text line’s height in the report layout might have inappropriately pushed its band’s exit events to the next page in Visual FoxPro 8. In Visual FoxPro 9, the band’s exit events occur on the same page. Additional revisions improve record-pointer-handling in footer bands, when bands stretch across pages. These changes are not specific to object-assisted output rendering. If you have relied on undocumented behavior providing exact band or layout control placement in a particular report, you should review that report’s behavior in Visual FoxPro 9.

    Rushmore Optimization

    When character values are indexed, all values are considered to be encoded using the table’s code page. In previous versions of Visual FoxPro, when the current Visual FoxPro code page differed from a table’s code page, any attempt to look for a character value within that table’s index resulted in an implicit translation of the value from the current Visual FoxPro code page into the table’s code page. This could cause SQL or other Rushmore optimizable commands to return or act upon incorrect records.

    In Visual FoxPro 9 and later, by default, the optimization engine no longer uses existing character indexes for tables created with a non-current code page. Instead, Visual FoxPro builds temporary indexes to ensure correct results. This can result in a loss of optimization of SQL or other commands which were optimized in earlier VFP versions. To prevent this, ensure that the current Visual FoxPro code page returned by CPCURRENT( ) Function matches the table’s code page returned by CPDBF( ) Function. This requires either changing the current Visual FoxPro code page, or changing the table’s code page. For information on specifying the current Visual FoxPro code page, see Understanding Code Pages in Visual FoxPro. For information on specifying the code page for a table, see How to: Specify the Code Page of a .dbf File. If you cannot change either the Visual FoxPro codepage or the table codepage to match, you can force optimization to work as it did in Visual FoxPro 8 and prior versions using the SET ENGINEBEHAVIOR Command with either 80 or 70 as a parameter.

    SQL SELECT Statements

    • A SELECT – SQL Command containing DISTINCT and ORDER BY clauses in which the ORDER BY field is not in the SELECT field list will generate an error in Visual FoxPro 9.0 with SET ENGINEBEHAVIOR 90 (Error 1808: SQL: ORDER BY clause is invalid.). The following example shows this:   Copy Code SET ENGINEBEHAVIOR 90 CREATE CURSOR foo (f1 int, f2 int) SELECT DISTINCT f1 FROM foo ORDER BY f2 INTO CURSOR res
    • A SELECT – SQL Command containing DISTINCT and HAVING clauses in which the HAVING field is not in the SELECT field list will now generate an error in Visual FoxPro 9.0 with SET ENGINEBEHAVIOR 90 (Error 1803: SQL: HAVING clause is invalid.). An error is reported because the HAVING field is not in projection and DISTINCT is used. The following example shows this:   Copy Code SET ENGINEBEHAVIOR 90 CREATE CURSOR foo (f1 int, f2 int) SELECT DISTINCT f1 FROM foo HAVING f2>1 INTO CURSOR res
    • The number of UNION statements that can be used in a SELECT – SQL Command is no longer limited to 9. Parentheses are not completely supported with UNION statements and unlike previous versions may generate an error. If two or more SELECT statements are enclosed in parenthesis, an error is generated during compile (Error 2196: Only a single SQL SELECT statement can be enclosed in parentheses.). This behavior is not tied to any SET ENGINEBEHAVIOR Command level. The following example shows this error:   Copy Code SELECT * FROM Table1 ;   UNION ;   (SELECT * FROM Table2 ;   UNION ;   SELECT * FROM Table3) The following example compiles without an error:   Copy Code SELECT * FROM Table1 ;   UNION ;   (SELECT * FROM Table2) ;   UNION ;   (SELECT * FROM Table3)

    For more information, see SET ENGINEBEHAVIOR Command.

    Disabling TABLEREVERT( ) Operations During TABLEUPDATE( ) Operations

    For CursorAdapters, Visual FoxPro does not permit TABLEREVERT( ) operations during operations.

    For more information, see TABLEREVERT( ) Function and TABLEUPDATE( ) Function.

    Index Key Truncation during Index Updates

    An error (Error 2199) is now generated when index key truncation is about to occur, typically during index creation or modification. This can happen with use of a key that contains an expression involving a Memo field, whose length in not fixed, such as in the following example:

    INDEX ON charfld1 + memofld1 TAG mytag

    Similar issues can also occur with the SQL engine (such as during a SQL SELECT command or View creation) where it might fail to build a temporary index to optimize a join evaluation if it is unable to accurately determine the maximum size of the key.

    For more information, see Error building key for index “name”. (Error 2199).

    Memo Field Corruption

    Visual FoxPro will now detect if a Memo field in a class library (.vcx) or form (.scx) is corrupt when you try to open up that file in the designer. If the file contains a corrupt Memo field, an Error 41 such as following will occur:

    Memo file <path>\myclass.VCT is missing or is invalid.

    Additionally, similar Memo errors may occur if you have a Visual FoxPro table open and try to access contents of a corrupt Memo. The following sample code shows how you can detect the Error 41 memo file corruption:

     Copy Code
    TRY   USE myTable EXCLUSIVE NOUPDATE   SCAN     SCATTER MEMO MEMVAR   ENDSCAN CATCH TO loError   IF loError.ErrorNo=41     * handle error here   ENDIF ENDTRY USE IN myTable

    While it is possible that loss of data may occur, the following sample code may assist in repairing some or the entire file:

     Copy Code
    ON ERROR * USE myclass.vcx COPY TO myclass_bkup.vcx&&backup COPY TO myclass2.vcx USE DELETE FILE myclass.vc* RENAME myclass2.vcx TO myclass.vcx RENAME myclass2.vct TO myclass.vct COMPILE CLASSLIB myclass.vcx ON ERROR

    Visual Form and Class Extended Property Support

    Visual FoxPro 9 allows you to create custom properties in your visual class (SCX or VCX file) whose values can contain carriage returns and/or be of length greater than 255 characters. If you specify a property with a value like this through the Properties Window (i.e., the Zoom dialog box), Visual FoxPro will store it in a format such that you will no longer be able to edit that class in older versions of Visual FoxPro.

    Class Definitions

    The ability to have a property assignment set to instantiated object is no longer supported in a class definition and will generate an error. The following example shows this.

     Copy Code
    LOCAL oCustom oCustom = CREATEOBJECT(‘cusTest’) DEFINE CLASS cusTest AS CUSTOM     oRef = CREATEOBJECT(‘myclass’) ENDDEFINE DEFINE CLASS myclass AS CUSTOM ENDDEFINE

    You can instead assign a property to an instantiated object reference in the Init event of your class.

    Merge Modules for Redistributable Components

    Visual FoxPro includes merge modules (MSM files) for use in redistributing shared components with your runtime applications. Merge modules are used by applications that can create Windows Installer based setups. For example, Visual FoxPro ships with merge modules that contain the Visual FoxPro runtime libraries as well as some common components including a number of ActiveX controls.

    For Visual FoxPro 9, the VFP9RUNTIME.MSM merge module contains the runtime libraries that you will need for your custom redistributable application. The VFP9RUNTIME.MSM merge module also has dependencies on the merge modules containing the Microsoft VC 7.1 runtime library (MSVCR71.DLL) and the GDI+ graphics library (GDIPLUS.DLL). Because of these dependencies, if you select the VFP9RUNTIME.MSM merge module in a Windows Installer tool such as InstallShield, the other dependent merge modules will automatically be selected as well.

    Note   For Windows XP and higher operating systems, Visual FoxPro uses the GDI+ graphics library that is installed in your Windows System folder.

    For Visual FoxPro 9, the merge module containing the VC runtime library no longer installs to the Windows System directory. Instead, this file is installed to your application’s directory. This is done in compliance with recommended component versioning strategies for Windows operating systems. The GDI+ library is installed into the same directory as your Visual FoxPro runtime libraries and is only installed on operating systems later than Windows XP (XP already includes the GDI+ library in its Windows System directory).

    Tip   There may be circumstances where you will want to install the VC or GDI+ library to another location such as the Windows System directory. You can do this with your Windows Installer application (e.g., InstallShield) by first selecting the merge module before selecting the VFP9RUNTIME.MSM one. Once you have selected a merge module, you can change its installation path.

    There are new merge modules for MSXML3 and MSXML4 XML parser components. The MSXML 3.0 component consists of the following merge modules:

    • MSXML 3.0 (msxml3_wim32.msm)
    • Msxml3 Exception INF Merge Module (msxml3inf_wim32.msm)
    • WebData std library (wdstddll_wim32.msm)

    There are two MSXML 4.0 modules that should be included with any custom setup:

    • MSXML 4.0 (msxml4sxs32.msm)
    • MSXML 4.0 (msxml4sys32.msm)

    MTDLL Memory Allocation

    Visual FoxPro contains a new PROGCACHE configuration file setting which specifies the amount of memory Visual FoxPro allocates at startup for running programs (program cache). This setting also determines memory allocated per thread for Visual FoxPro MTDLL COM Servers. In prior versions, this setting was not configurable and memory was allocated as a fixed program cache of a little over 9MB (144 * 64K). The new PROGCACHE setting allows you to set the exact size of the program cache or specify that dynamic memory allocation be used.

    Since MTDLL COM Servers can use up a great amount of memory if many threads are created, it is important that memory be allocated more efficiently for these scenarios. In Visual FoxPro 9, the new default setting for MTDLL COM Servers is -2 (dynamic memory allocation). For more information, see Special Terms for Configuration Files.

    Miscellaneous Changes

    The following are miscellaneous changes that you should know about but are not likely to impact existing code. CursorAdapter Changes

    In the current version of Visual FoxPro, the following behavior changes apply to the CursorAdapter object:

    Grid SetFocus Supported for AllowCellSelection

    You can now call a Grid control’s SetFocus Method and have the Grid receive focus when the AllowCellSelection Property is set to False (.F.) and the grid contains no records.

    EXECSCRIPT Function

    The EXECSCRIPT( ) Function now allows you to pass parameters by reference.

    Additionally, Visual FoxPro 9.0 tightens syntax validation of calls made from concatenation of parameters. The following code, which worked in prior versions of Visual FoxPro, now properly causes an error because the CHR(13) character breaks the call into two lines whereas it is supposed to be part of the parameter for the EXECSCRIPT call.

     Copy Code
    cRecPauseScript = “EXECSCRIPT(‘” + ; “?123” + CHR(13) + ; “?456” + ; “‘)” _VFP.DoCmd(cRecPauseScript)

    To make a valid call that does not cause a syntax error, you can use the following code:

     Copy Code
    cRecPauseScript = “EXECSCRIPT(‘?123’+CHR(13)+ ‘?456’)” _VFP.DoCmd(cRecPauseScript)

    Listbox Control Click Event

    In the current version of Visual FoxPro, the PageUp, PageDown, Home and End keyboard keys now cause a Listbox control’s Click event to fire. In previous versions, these keys did not trigger the Click event to fire, unlike the arrow keys.

    PEMSTATUS( ) Function Returns False for Hidden Native Properties

    In previous versions of Visual FoxPro, the PEMSTATUS( ) function returned True (.T.) for hidden native properties of Visual FoxPro base classes when specifying a value of 5 for nAttribute. In the current release, PEMSTATUS( ) returns False (.F.) for these hidden native properties. For more information, see PEMSTATUS( ) Function.

    Changes to Options Dialog Box

    • In the Options dialog box, the List display count option has been moved from the Editor tab to the View tab. For more information, see View Tab, Options Dialog Box.
    • In previous versions of Visual FoxPro, you could output all the settings in the Options Dialog Box (Visual FoxPro) to the Command Window by pressing the SHIFT key when choosing the OK button to close the dialog. In the current release, these settings are now sent to the Debug Output Window. The Debug Output window must be opened in order for the settings to be directed there.

    FOXRUN.PIF

    The FOXRUN.PIF file that is used by the RUN | ! Command is no longer installed in the Visual FoxPro root directory. If Visual FoxPro detects the presence of a FOXRUN.PIF file during a RUN command, it will use COMMAND.COM to execute the specified RUN command. This may not be the desired SHELL program to use for a particular operating system, especially newer ones like Windows XP in which CMD.EXE is preferable.

    The current behavior for a RUN command without the existence of a FOXRUN.PIF file is that the RUN command will use the SHELL program specified by the operating system COMSPEC environment variable. With Windows XP, you can view and edit this variable by right-clicking your computer desktop’s My Computer icon and selecting the Properties dialog box (Advanced tab).

    The FOXRUN.PIF file is still available in the Tools directory if you need it for a particular reason.

    For more information, see RUN | ! Command.

    SCATTER Command

    The SCATTER command no longer allows for ambiguous use of both MEMVAR and NAME clauses in the same command. You can only include one of these clauses. In prior versions, the following code would not generate an error:

     Copy Code
    USE HOME()+”SAMPLES\Data\customer.dbf” SCATTER MEMVAR NAME oCust

    For more information, see SCATTER Command.

    SET DOHISTORY

    The SET DOHISTORY command, which is included for backward compatibility, was updated to send output to the Debug Output Window instead of the Command Window as in prior versions.

    SCREEN ShowTips Property

    The default value for _SCREEN ShowTips Property has been changed from False (.F.) to True (.T.). This change was made because new Memo and Field Tips support is now dependent on this setting.

    AllowCellSelection Does Not Permit Deleting Grid Rows When Set to False

    When the AllowCellSelection Property is set to False (.F.) for a Grid control, you cannot select a row for deletion by clicking the deletion column. For more information, see AllowCellSelection Property.

    Northwind Database

    The sample Northwind database has been updated. Five of the stored procedures now include calls to the SETRESULTSET( ) Function so that the Visual FoxPro OLE DB Provider will return a cursor when they are executed.

    Foundation Classes

    The _ShellExecute class contained in the _Environ.vcx FFC class library has been updated to include an additional parameter in the ShellExecute method.

    Wizards and Builders

    The Wizard/Builder selection dialog box now properly hides deleted entries in the Wizard and Builder registration tables.

    Specifying Western Language Script Values for GETFONT( ) Function

    In versions prior to this release, passing 0 as the nFontCharSet value for GETFONT( ) opened the Font Picker dialog box and displayed the Script list as unavailable. You could not specify 0 (Western) as the language script value, and setting it to 1 (Default) sets nFontCharSet to the default font setting only, which is determined by the operating system.

    In this release, passing 0 to GETFONT( ) opens the Font Picker dialog box with the Script list available and Western selected. The return value for GETFONT( ) also includes the return value for nFontCharSet.

    Removed Items

    HTML Help SDK

    The HTML Help 1.3 SDK no longer ships with Visual FoxPro.

    See Also

    Concepts

    What’s New in Visual FoxPro

    Guide to Reporting Improvements

    Data and XML Feature Enhancements

    SQL Language Improvements

    Class Enhancements

    Language Enhancements

    Interactive Development Environment (IDE) Enhancements

    Enhancements to Visual FoxPro Designers

    Miscellaneous Enhancements

    Visual FoxPro New Reserved Words

    The following tables list new words added to the Visual FoxPro language which are now reserved:

    _

    _MEMBERDATA_MENUDESIGNER_REPORTBUILDER
    _REPORTOUTPUT_ REPORTPREVIEW_TOOLTIPTIMEOUT

    A

    ADJUSTOBJECTSIZEADOCODEPAGEAFTERBAND
    AFTERRECORDREFRESHAFTERREPORTALLOWMODALMESSAGES
    ANCHORASQLHANDLESAUTOCOMPLETE
    AUTOCOMPSOURCEAUTOCOMPTABLEAUTOHIDESCROLLBAR

    B

    BEFOREBANDBEFORERECORDREFRESHBEFOREREPORT
    BLOB

    C

    CANCELREPORTCASTCLEARRESULTSET
    CLEARSTATUSCOMMANDCLAUSESCONFLICTCHECKCMD
    CONFLICTCHECKTYPECURRENTDATASESSIONCURRENTPASS

    D

    DECLAREXMLPREFIXDELAYEDMEMOFETCHDISPLAYORIENTATION
    DOCKABLEDOMESSAGEDOSTATUS
    DYNAMICLINEHEIGHT

    E

    EVALUATECONTENTS

    F

    FETCHMEMOCMDLISTFETCHMEMODATASOURCEFETCHMEMODATASOURCETYPE
    FIRSTNESTEDTABLEFRXDATASESSIONFOXOBJECT

    G

    GDIPLUSGRAPHICSGETAUTOINCVALUEGETDOCKSTATE
    GETPAGEHEIGHTGETPAGEWIDTHGETRESULTSET

    I

    ICASEINCLUDEPAGEINOUTPUTINSERTCMDREFRESHCMD
    INSERTCMDREFRESHFIELDLISTINSERTCMDREFRESHKEYFIELDLISTISMEMOFETCHED
    ISPENISTRANSACTABLE

    L

    LISTENERTYPELOADREPORT

    M

    MAKETRANSACTABLEMAPBINARYMAPVARCHAR

    N

    NESTNESTEDINTONEXTSIBLINGTABLE

    O

    ONPREVIEWCLOSEOPTIMIZEORDERDIRECTION
    OUTPUTPAGEOUTPUTPAGECOUNTOUTPUTTYPE

    P

    PAGENOPAGETOTALPICTUREMARGIN
    PICTURESPACINGPICTUREVALPOLYPOINTS
    PREVIEWCONTAINERPRINTJOBNAMEPROGCACHE

    Q

    QUIETMODE

    R

    RECORDREFRESHREFRESHALIASREFRESHCMD
    REFRESHCMDDATASOURCEREFRESHCMDDATASOURCETYPEREFRESHIGNOREFIELDLIST
    REFRESHTIMESTAMPRENDERREPORTBEHAVIOR
    REPORTLISTENERRESPECTNESTINGROTATION

    S

    SCCDESTROYSCCINITSELECTIONNAMESPACES
    SENDGDIPLUSIMAGESETRESULTSETSQLIDLEDISCONNECT
    SUPPORTSLISTENERTYPE

    T

    TABLEPROMPTTIMESTAMPFIELDLISTTWOPASSPROCESS

    U

    UNLOADREPORTUNNESTUPDATECMDREFRESHCMD
    UPDATECMDREFRESHFIELDLISTUPDATECMDREFRESHKEYFIELDLISTUPDATESTATUS
    USECODEPAGEUSECURSORSCHEMAUSETRANSACTIONS

    V

    VARBINARYVARCHARVARCHARMAPPING

    X

    XMLNAMEISXPATH

    See Also

    Concepts

    What’s New in Visual FoxPro

    Reserved Words (Visual FoxPro)

    Requirements for Installing Visual FoxPro

    Visual FoxPro has the following minimum system requirements:

    • Computer: PC with a Pentium class processor.
    • Peripherals: Mouse or pointing device
    • Memory: 64 MB RAM (128 MB or higher recommended)
    • Hard disk space:
      • Visual FoxPro Prerequisites: 20 MB
      • Visual FoxPro Typical Install: 165 MB
      • Visual FoxPro Maximum Install: 165 MB
    • Video: 800 x 600 resolution, 256 colors (High color 16-bit recommended)
    • Operating system: Developing applications with Visual FoxPro is supported only on Microsoft Windows 2000 Service Pack 3 or later, Windows XP and Windows Server 2003. You can create and distribute run-time applications for Windows 98, Windows Me, Windows 2000 Service Pack 3 or later, Windows XP and Windows Server 2003. Note: Installation on Windows NT 4.0 Terminal Server Edition is not supported.

    See Also

    Concepts

    How to: Install Visual FoxPro

    Installing Visual FoxPro

    How to: Install Visual FoxPro

    You can install this version of Visual FoxPro from a CD-ROM or a network to a local hard drive. You must install Visual FoxPro on a local drive, not a mapped drive. There is no other preparation required before installing Visual FoxPro. You must have administrator privileges to install Visual FoxPro. It is recommended that you run with power-user privileges to use all the provided tools effectively.

    You can safely install or uninstall using Visual FoxPro Setup. If you are upgrading Visual FoxPro, you must first uninstall the previous version of of the program. Though both versions of Visual FoxPro can exist on the same computer, you cannot install the current version of Visual FoxPro in the same directory as the previous version.

    If you plan to publish XML Web services using Visual FoxPro, you might want to set up Internet Information Services (IIS) on a Windows 2000, Windows XP or Windows Server 2003 computer. Refer to your operating system documentation for instructions on how to set up and configure IIS.

    Note:
    Visual FoxPro setup no longer installs any Windows operating system Service Packs or versions of Internet Explorer. It is highly recommended that you install the latest versions of these components before installing Visual FoxPro. Additionally, Visual FoxPro 9.0 is supported only on Windows 2000 Service Pack 3 or later. For details about installing the latest Service Pack, visit the following Microsoft Web page at http://www.microsoft.com/windows2000/.

    Full installation includes all Visual FoxPro program files, online help, and samples files.

    To install Visual FoxPro

    1. Quit all open applications. Note: If you use a virus protection program on your computer, override it or turn it off before running the Installation wizard. The Installation wizard might not run properly with virus protection turned on. After installation, be sure to restart your virus protection program.
    2. Insert the Visual FoxPro CD. The Visual FoxPro Setup start page appears automatically.
    3. Click Install Visual FoxPro to launch Visual FoxPro Setup.
    4. To determine if you need additional components, click Prerequisites to display any necessary components.
    5. Click Install Now! to install any new components. If Visual FoxPro Prerequisites needs to only update components, click Update Now!
    6. You might need to restart your computer. When finished, click Done. Visual FoxPro Setup reappears.
    7. To continue installation, click Visual FoxPro.
    8. After accepting the End User License Agreement and entering the Product Key and your name, click Continue. Note: Visual FoxPro cannot be installed on a mapped drive. You must install Visual FoxPro on a local drive. Do not attempt to use the Map Network Drive functionality in Setup.
    9. On the Options page, select the features you want to install and click Install Now! to continue.
    10. When finished, click Done to return to Visual FoxPro Setup. Click Exit to return to the Visual FoxPro Setup start page.

    If you uninstall Visual FoxPro while the previous version of Visual FoxPro exists on your computer, certain shared registry keys used by the previous version of Visual FoxPro are removed. You must reinstall these critical shared registry keys.

    If you run Visual FoxPro from the Start menu, Visual FoxPro Setup automatically reinstalls these keys. If you start Visual FoxPro using other means, such as running the application executable directly, the setup program does not start automatically. You should use Add/Remove Programs in the Control Panel and the following steps to reinstall the registry keys manually:

    To manually reinstall Visual FoxPro 9.0 registry keys

    1. From the Start menu, click Control Panel.
    2. Click Add/Remove Programs.
    3. Click Change/Remove for Microsoft Visual FoxPro 9.0.
    4. Click Visual FoxPro and Repair/Reinstall.

    See Also

    Concepts

    Requirements for Installing Visual FoxPro

    How to: Install Additional Applications

    How to: Reinstall Visual FoxPro

    Troubleshooting Installation

    Installing Visual FoxPro

    How to: Install Additional Applications

    This release includes copies of additional software that you can install and use with Visual FoxPro. These include:

    • InstallShield Express Limited Edition Provides the capability to package and deploy the applications that you create using Visual FoxPro. Visual FoxPro includes the InstallShield Express 5.0 Visual FoxPro Limited Edition. Note: The limited and full editions of InstallShield Express 5.0 are considered two versions of the same product and cannot coexist. If you install one version on a computer where another already exists, the original is uninstalled automatically. Because the limited edition contains fewer features than the full edition, you should keep the full edition on your computer.
    • Microsoft SOAP Toolkit 3.0 Samples Provides samples for demonstrating how to consume and publish XML Web services. Visual FoxPro Prerequisites installs the core SOAP Toolkit 3.0 components needed to access and publish XML Web services in Visual FoxPro.
    • Microsoft SQL Server 2000 Desktop Engine (MSDE) Provides a personal version of SQL Server.

    To install InstallShield Express Limited Edition

    1. Insert the Visual FoxPro CD. The Visual FoxPro Setup start page opens automatically.
    2. Click Install InstallShield Express.
    3. Follow the instructions in the InstallShield Express installation wizard.

    You can also locate the Setup.exe file for InstallShield Express in the InstallShield folder on the Visual FoxPro CD.

    Note:
    Visual FoxPro 9.0 installs its redistributable merge modules in the same location as Visual FoxPro 8.0.

    The version of InstallShield Express included with Visual FoxPro 9.0 automatically uses the Visual FoxPro 9.0 merge module location.

    Note:
    Visual FoxPro 9.0 requires certain merge modules when creating a Visual FoxPro 9.0 redistributable custom application setup program using InstallShield Express.

    You need to include the following merge modules when creating your custom setup program:

    • Microsoft Visual FoxPro 9 Runtime Libraries
    • Microsoft Visual C Runtime Library 7.1
    • GDI Plus Redist
    • MSXML 4.0
    • MSXML 3.0 (needed only for CURSORTOXML functions)
    • Microsoft Visual FoxPro 9 Runtime Language Libraries (specific language library files that may be needed for international applications)
    • Reporting Applications (needed for Visual FoxPro 9.0 reporting engine)
    Note:
    MSXML 4.0 consists of two merge modules (msxml4sxs32.msm and msxml4sys32.msm). MSXML 3.0 consists of three merge modules (msxml3_wim32.msm, msxml3inf_wim32.msm and wdstddll_wim32.msm).

    To install SOAP Toolkit 3.0 Samples

    1. Insert the Visual FoxPro CD. The Visual FoxPro Setup start page opens automatically.
    2. Click Install SOAP Toolkit 3.0 Samples.
    3. Follow the instructions in the SOAP Toolkit 3.0 Samples Setup Wizard.

    You can also locate the Soapsdk.msi and Soapsamp.msi files for the SOAP Toolkit in the SOAPToolkit folder on the Visual FoxPro CD.

    To install MSDE

    1. Insert the Visual FoxPro CD. The Visual FoxPro Setup start page opens automatically.
    2. Click Install Microsoft SQL Server Desktop Engine (MSDE) and follow the installation instructions that appear in the Readme file.

    You can locate the Setup.exe file for MSDE in the SQLMSDE folder on the Visual FoxPro CD.

    Note:
    Visual FoxPro includes Microsoft SQL Server 2000 Desktop Engine Service Pack 3.0a. To make sure you have the most recent version and Service Pack installed, visit the Microsoft SQL Server Web page at http://www.microsoft.com/sql. In addition, if you are distributing custom Visual FoxPro applications that require MSDE, you can obtain the redistributable merge modules from the Microsoft SQL Server Web page for use with Windows Installer-based setup programs.

    See Also

    Concepts

    How to: Install Visual FoxPro

    How to: Reinstall Visual FoxPro

    You can reinstall Visual FoxPro by uninstalling it and then installing it again. You can uninstall Visual FoxPro from the Start menu or from the original installation disk.

    To uninstall Visual FoxPro

    1. On the Start menu, click Control Panel.
    2. In the Control Panel window, double-click Add or Remove Programs. The Add or Remove Programs window opens.
    3. In the Currently installed programs list, click the version of Microsoft Visual FoxPro you want to uninstall, and then Change/Remove.

    If you reinstall Visual FoxPro or reinstall to another location, you might want to clean your user settings and other files installed by Visual FoxPro before reinstalling.

    You can remove these files by deleting the contents of the …\Application Data\Microsoft\Visual FoxPro folder inside your user settings folder. To determine the location of the Application Data folder, type ? HOME(7) in the Command window. These files include your FoxUser.* resource files, which contain user settings, and folders for the Toolbox and Task Pane.

    However, it is possible that your resource files are in another location. You can determine their location by typing the following in the Command window:

     Copy Code
    ? SYS(2005)

    You should delete old Code Reference files that might be associated with projects in the project directories. These are labeled as projectname_ref.* files. You might also need to restore the default Visual FoxPro registry settings.

    Visual FoxPro includes the VFPClean.app tool so you can make sure all core Xbase and other files are set appropriately.

    To run VFPClean.app

    • Type the following line of code in the Command window:   Copy Code DO HOME()+”VFPCLEAN.APP”

    See Also

    Concepts

    How to: Install Visual FoxPro

    Installing Visual FoxPro

    Troubleshooting Installation

    You might encounter the following issues when installing Visual FoxPro:

    • If you cannot run Visual FoxPro and do not see error messages telling you what is wrong, the problem might be in your computer’s ROM BIOS or the video driver you are using.
    • If you are using an extended keyboard, be sure it is compatible with the ROM BIOS. In addition, make sure that you are using a standard VGA or Super VGA Windows video driver.
    • If you get a “stack overflow” error message, your video driver is out of date or not designed for your video card. To correct this problem, update the video driver.
    • For additional information, see the Visual FoxPro Readme at the root of the Visual FoxPro installation CD.

    See Also

    Concepts

    Installing Visual FoxPro

    Upgrading from Earlier Versions

    Microsoft Visual FoxPro protects your investment in applications built with previous versions of FoxPro. In Visual FoxPro, you can run many applications that were written in earlier versions with little or no conversion. You can modify and enhance applications using the Visual FoxPro language, knowing that most extensions to the language do not affect backward compatibility. In addition, you can convert FoxPro screens, projects, and reports to Visual FoxPro format.

    However, it is possible that some behavior or feature changes in the current version of Visual FoxPro might affect existing Visual FoxPro source code. Therefore, you should review the new features, enhancements, and most recent behavior changes for this version. For more information, see What’s New in Visual FoxPro and Changes in Functionality for the Current Release.

    Conversion to Visual FoxPro Format

    If you choose to convert your dBASE or FoxPro files to the Visual FoxPro format, you can take advantage of the unique features of Visual FoxPro. You can run many files from some previous versions of FoxPro directly; others require varying levels of conversion.

    You can convert most projects or components created using previous versions of Visual FoxPro simply by opening or recompiling them in this version of Visual FoxPro. When you recompile components, such as forms, screens, or reports, some modifications may be necessary. You can make modifications to these components in the same way you modify the components of this version of Visual FoxPro.

    You can find additional information about upgrading from previous versions of Visual FoxPro on the Microsoft Developer Network (MSDN) Web site at http://msdn.microsoft.com. You can search the MSDN Archive for documentation of previous versions of Visual FoxPro.

    See Also

    Concepts

    Getting Started with Visual FoxPro

    Overview of Visual FoxPro Features

    Installing Visual FoxPro

    Customizing the Visual FoxPro Environment

    Optimizing Your System

    How to: Convert Earlier Visual FoxPro Files

    You can explicitly convert FoxPro 2.6 and Visual FoxPro 3.0 files to the current Visual FoxPro format, which is necessary when you want to use these files with later versions of Visual FoxPro. Files that are created from later versions are converted automatically.

    To convert FoxPro 2.6 and Visual FoxPro 3.0 files

    1. On the File menu, click Open.
    2. In the Open dialog box, browse for and select the file. The Visual FoxPro Converter dialog box opens. For more information, see Visual FoxPro Converter Dialog Box.
    3. In the Visual FoxPro Converter dialog box, select the options you want.
    4. To complete the file conversion, click Continue. Note: If you are converting Macintosh or MS-DOS files that have never contained Windows records, the Visual FoxPro Transporter dialog box appears. For more information, see Visual FoxPro Transporter Dialog Box.

    You can also convert FoxPro 2.6 and Visual FoxPro 3.0 files by typing one of the following commands with the file name in the Command window:

    See Also

    Concepts

    Getting Started with Visual FoxPro

    Upgrading from Earlier Versions

    Optimizing Your System

    Visual FoxPro is designed to be a fast relational database development system. However, applications you create with Visual FoxPro can have varying requirements and purposes. Therefore, you might want to optimize the operating system, Visual FoxPro, or your application for maximum performance.

    In This Section

    Optimizing the Operating Environment

    Describes how to optimize computer hardware and and operating environment for running Visual FoxPro. Optimizing Visual FoxPro Startup Speed

    Describes how to optimize startup and operating speed in Visual FoxPro.Optimizing Visual FoxPro in a Multiuser Environment

    Describes how to improve performance when running Visual FoxPro in a multiuser environment.

    Related Sections

    Customizing the Visual FoxPro Environment

    Provides information about setting environment options, accessibility features, and configuration.Getting Started with Visual FoxPro

    Discusses how to get started, including information about installing, upgrading, and customizing Visual FoxPro to create state-of-the-art enterprise database solutions. What’s New in Visual FoxPro

    Lists the new features and enhancements made to this version of Microsoft Visual FoxPro.Using Visual FoxPro

    Provides links to information on Visual FoxPro programming features that are designed to improve developer productivity, including Access and Assign methods, support for more graphic file formats, and language to simplify programming tasks.Developing Visual FoxPro Applications

    Includes conceptual information about how to develop Visual FoxPro applications, instructions for creating databases and the user interface, and other tasks needed to create Visual FoxPro applications.Programming in Visual FoxPro

    Discusses how to access the full power of Visual FoxPro by creating applications. Understanding object-oriented programming techniques and the event-driven model can maximize your programming productivity.

    Optimizing the Operating Environment

    You can optimize Visual FoxPro performance by maximizing your computer’s hardware and operating environment. The following sections describe how you can optimize these areas:

    Maximizing Memory and Virtual Memory

    Providing your computer with as much memory as possible is the most effective way to optimize your system for Visual FoxPro. You can also use memory more effectively by closing all other running applications on your computer. To maximize the use of your computer’s memory while running Visual FoxPro, follow these guidelines:

    • Do not run other Windows applications while running Visual FoxPro.
    • Use only those memory-resident programs needed for operation.
    • Simplify the screen display.

    You can free memory by simplifying the way windows and screen backgrounds display on your computer monitor.

    • Use a color or a pattern for the desktop background instead of wallpaper.
    • Use the lowest-resolution display that is practical for you. The higher resolution of the display, the more memory your computer requires and the slower your graphics and user-interface elements appear. For VGA-compatible displays that use an extended mode driver, such as Video 7 or 8514, using the standard VGA driver ensures faster display performance but provides lower resolution and less color support.

    To increase the number of applications that you can run simultaneously, Microsoft Windows supports virtual memory by swapping segments of code that is the least recently used from memory to the hard disk in the form of a paging file. As a rule, the default settings in the Windows operating system for managing virtual memory meet the requirements of most users and are the recommended settings.

    Note:
    The paging file does not improve Visual FoxPro performance and is not a substitute for more memory.

    Managing Your Hard Disk

    Managing your hard disk can improve overall product speed. To get the best performance from your hard disk, provide a generous amount of disk space. If your hard disk has little free space, you can increase Visual FoxPro performance by removing unnecessary data or by purchasing a hard disk with greater capacity. Disk input/output performance degrades significantly when a hard disk is nearly full. The more free hard disk space that is available, the more likely it is that contiguous blocks of disk space are available. Visual FoxPro uses this space for changes and additions to database, table, index, memo, and temporary files. Increasing free hard disk space improves performance of any commands that change or add to your files. More disk space also decreases the time required to read those files in response to your queries. The way that Windows and Visual FoxPro manage files on disk can greatly affect the performance of your application. The following sections discuss managing files in directories and temporary files:

    Managing Files in Directories

    As a directory becomes increasingly congested with files, the operating system takes longer to find files. The speed of your system when searching directories is a factor that Visual FoxPro does not control. To improve the speed of directory searches, reduce the number of files in your directories by performing the following actions: Use the Visual FoxPro Project Manager to create and manage your files, segregate program files into separate directories, and avoid creating numerous generated files. When you want to distribute your application, create an application or an executable (.exe) file instead of numerous individually generated files. This process decreases the number of files in your application’s subdirectories and increases performance. If you delete a large number of files in one directory, copy the remaining files into a new directory or optimize the directory using a defragmenting utility program. Note: Deleting files from a directory does not automatically speed directory searching. When a file is deleted, the file is only marked for deletion and is still included in directory searches. When saving files, use short file paths to increase performance. For example, suppose you have a file path “C:\Program Files\Microsoft Visual FoxPro\…”, which is a very long file path. Try to use shorter file paths. Managing Temporary Files

    Visual FoxPro creates temporary files for a variety of operations. For example, Visual FoxPro creates temporary files during editing, indexing, and sorting. Text editing sessions can also create a temporary or backup (.bak) copy of the edited file. By default, Visual FoxPro creates its temporary files in the same directory that Windows stores its temporary files unless you specifically designate an alternate location. Tip: In most cases, you should specify one location for all Visual FoxPro temporary files. Make sure that the location you specify contains enough space for all possible temporary files.

    For more information, see How to: Specify the Location of Temporary Files.

    Searching for Temporary Files

    When Visual FoxPro searches for temporary files, for example, when you use the SYS(2023) – Temporary Path function to retrieve the temporary files path or when the TMPFILES, EDITWORK, PROGWORK, and SORTWORK settings in a Visual FoxPro configuration file do not specify a different location, the Windows API GetTempPath is used to search for the path containing the temporary files. GetTempPath searches a sequence of variables that differ depending on the operating system. Microsoft Windows 2000 and later include user variables that store the location of temporary files, while Microsoft Windows 95, 98, and Me include only global system environment variables for this purpose.

    On Windows 2000 and later, GetTempPath, and therefore, SYS(2023), TMPFILES, EDITWORK, PROGWORK, and SORTWORK, searches the TMP user variable for the location of temporary files by default. If the TMP user variable does not specify a location, Visual FoxPro searches the following variables in a specific order:

    • TMP system variable.
    • TEMP user variable.
    • TEMP system variable.

    If these variables do not specify a location, the location for storing temporary files defaults to the home drive and path, or the Temp folder in the user’s Documents and Settings directory.

    Note:
    If more than one value is specified for TMP or TEMP, then the first value is used.

    On Windows 95, 98, and Me, GetTempPath searches the TMP and TEMP global system variables in that order and then searches the current directory.

    For more information, see SYS(2023) – Temporary Path and Special Terms for Configuration Files.

    See Also

    Concepts

    Optimizing Visual FoxPro in a Multiuser Environment

    Optimizing Visual FoxPro Startup Speed

    Optimizing Your System

    Optimizing Visual FoxPro Startup Speed

    Though Visual FoxPro is always fast, you can optimize the startup and operating speed. This section describes enhancing Visual FoxPro performance by managing startup speed and optimizing SET commands.

    Managing Startup Speed

    The time required to load and start Visual FoxPro relates to the physical size of Visual FoxPro, the length of the PATH statement in effect, the number of items to be found at startup, and other factors. You can control the load size, the search path, component file locations, and the startup SET command values of Visual FoxPro.

    Managing File Locations

    Visual FoxPro stores the FoxUser.dbf file, which contains user settings, in the user’s Application Data directory by default. You can display this location by typing ? HOME(7) in the Command window. Visual FoxPro searches for the FoxUser.dbf and Config.fpw files in the following places:

    • In the startup application or executable file, if any. For example, you can start a Visual FoxPro application by typing the following code on the command line:   Copy Code VFPversionNumber.exe MyApp.app  – or –   Copy Code VFPversionNumber.exe MyApp.exe If the startup application or executable file contains a Config.fpw file, the configuration file is always used. You can override settings in a Config.fpw file that are bound inside an application by specifying an external Config.fpw file, using the -C command-line switch when starting an application or Visual FoxPro.
    • In the working directory.
    • Along the path established with the PATH environment variable.
    • In the directory containing Visual FoxPro.

    Controlling File Loading

    You can also speed startup by preventing Visual FoxPro from loading files you don’t plan to use. If your application does not use the FoxUser or FoxHelp file, disable them in the Config.fpw file by using the following commands:

     Copy Code
    RESOURCE = OFF HELP = OFF

    Visual FoxPro seeks all other Visual FoxPro components (GENXTAB, CONVERT, and so on) only in the Visual FoxPro directory. If you place components elsewhere, you must explicitly identify the path to those components in your Config.fpw file. For example, you might specify these locations:

     Copy Code
    _TRANSPORT = c:\migrate\transport.prg _GENXTAB = c:\crosstab\genxtab.prg _FOXREF = c:\coderefs\foxref.app

    You can use the environment variable FOXPROWCFG to explicitly specify the location of Config.fpw. For details about the FOXPROWCFG variable, see Customizing the Visual FoxPro Environment.

    Optimizing the Load Size of Visual FoxPro

    If you don’t plan on using any of the Visual FoxPro components listed previously, set them to an empty string to speed startup.

    To optimize the load size of Visual FoxPro, use the following syntax:

     Copy Code
    cFileVariable = “”

    Replace cFileVariable with _TRANSPORT, _CONVERT, or other variables as appropriate.

    Optimizing Key SET Commands

    You can optimize the operation of Visual FoxPro by tuning the values of certain SET commands.

    The following table shows SET commands that have the greatest effect on performance, and their settings for maximum performance. You can specify SET command values by including them in the Config.fpw file, by typing them in the Command window, or by setting them in the Options dialog box.

    Command Settings for Maximum Performance

    SET CommandPerformance Setting
    SET ESCAPE CommandON
    SET OPTIMIZE CommandON
    SET REFRESH Command0,0
    SET SYSMENU CommandDEFAULT
    SET TALK CommandOFF
    SET VIEW CommandOFF

    See Also

    Concepts

    Optimizing Your System

    Optimizing Visual FoxPro in a Multiuser Environment

    Optimizing the Operating Environment

    Customizing the Visual FoxPro Environment

    SET ESCAPE Command

    SET REFRESH Command

    SET SYSMENU Command

    Command Window (Visual FoxPro)

    Optimizing Visual FoxPro in a Multiuser Environment

    When you run Visual FoxPro or Visual FoxPro applications in a multiuser environment, you can improve performance by managing storage of temporary files and controlling the way tables are shared.

    Managing Temporary Files

    In most multiuser environments, it is recommended that you save temporary files to local disks or memory when networked computers contain large amounts of free disk space. Redirecting storage of temporary files can improve performance by reducing frequent access to the network drive.

    On small networks with older networked computers and slow hard disks, you might experience better performance by leaving Visual FoxPro temporary files on the file server; however, when in doubt, direct temporary files to the local disk. When working on large, heavily used networks, always redirect temporary files to the local disk.

    By saving all temporary files to a single directory on a local hard drive, you can safely erase the contents of the temporary file directory on the file server prior to each Visual FoxPro session. This action purges the system of any temporary files that were created but not erased by Visual FoxPro due to a system reboot or power loss.

    For more information about temporary files, see Optimizing the Operating Environment and How to: Specify the Location of Temporary Files.

    Sharing Tables

    If users share tables on a network, the way you manage access to them can affect performance.

    • Avoid opening and closing tables repeatedly.
    • Buffer write operations to tables that are not shared.
    • Provide exclusive access to tables.
    • Limit the time on locking tables.

    Providing Exclusive Access

    You can enhance performance for the APPEND, REPLACE, and DELETE commands and operations that run at times when no other users require access to the data, for example, overnight updates, by opening data files for exclusive use. When tables are open for exclusive use, performance improves because Visual FoxPro does not need to test the status of record or file locks.

    To open data files for exclusive use, use the EXCLUSIVE clause in the USE and OPEN DATABASE commands. For more information, see USE Command and OPEN DATABASE Command.

    Limiting the Time on Locking Tables

    You can reduce contention between users for write access to a table or record by shortening the amount of time for locking a record or table. Instead of locking a record while the user edits it, lock the record only after it has been edited. Using optimistic row buffering provides the shortest amount of time that records are locked. For more information, see Buffering Data.

    See Also

    Concepts

    Optimizing Applications in Multiuser Environments

    Optimizing Your System

    Visual FoxPro Configuration

    The configuration of Visual FoxPro determines how your copy of Visual FoxPro looks and behaves. For example, you can establish the default locations for files used with Visual FoxPro, how your source code looks in an edit window, and the format of dates and times.

    You can make changes to the Visual FoxPro configuration that exist for the current session only (temporary), or specify them as the default settings for the next time you start Visual FoxPro (permanent). If the settings are temporary, they are stored in memory and are discarded when you quit Visual FoxPro.

    If you make permanent settings, they are stored in the Microsoft Windows registry or Visual FoxPro resource file. The Windows registry is a database that stores configuration information about the operating system, all Windows applications, OLE, and optional components such as ODBC. For example, the registry is where Windows stores the associations between file name extensions and applications so that when you click a file name, Windows can launch or activate the appropriate application.

    For an example of how to change the registry, you can examine Registry.prg in the \Samples\Classes directory, which contains numerous methods based on Windows API calls and makes it possible for you to manipulate the Windows registry.

    Similarly, Visual FoxPro stores its application-specific configuration information in the registry. When you start Visual FoxPro, the program reads the configuration information in the registry and sets the configuration according to those settings. After reading the registry, Visual FoxPro also checks for a configuration file, which is a text file in which you can store configuration settings to override the defaults stored in the registry. After Visual FoxPro has started, you can make additional configuration settings using the Options Dialog Box or SET commands. For more information, see How to: View and Change Environment Settings.

    Note:
    The run-time version of Visual FoxPro does not read the Windows registry when starting up, as registry settings are designed primarily to configure the development environment. If you intend to distribute your Visual FoxPro applications using a run-time library, you can establish configuration settings in two ways: with a configuration file, or with a program that manipulates the Windows registry on the user’s computer.

    Visual FoxPro also maintains a resource file, Foxuser.dbf, that stores information about the current state of the program when you quit. For example, the resource file contains information about the location and size of the Command window, current keyboard macros, the toolbars that are displayed, and so on. The Foxuser.dbf file is an ordinary Visual FoxPro table, which you can read and change as required by your application.

    Tip:
    If the data in the Foxuser.dbf file becomes corrupted or invalid, it can cause Visual FoxPro to behave in an erratic manner. If you do not manually store anything in the table, for example keyboard macros, deleting the table might help.

    See Also

    Concepts

    Customizing the Visual FoxPro Environment

    How to: View and Change Environment Settings

    Options Dialog Box (Visual FoxPro)

    ODBC Registry Foundation Class

    Command Window (Visual FoxPro)

    How to: Change Configuration Settings in the Windows Registry

    SET RESOURCE Command


    Help from Microsoft Website

    Microsoft Transaction Server for Visual FoxPro Developers 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. What Is Microsoft Transaction Server?
    3. Why Is MTS Important for Visual FoxPro Developers?
    4. Creating Your First MTS Server

    Show 12 more

    Randy Brown
    Microsoft Corporation

    October 1998

    Summary: Discusses using Microsoft® Visual FoxPro® version 6.0 with MTS to develop three-tier applications. (36 printed pages).

    Contents

    Introduction What Is Microsoft Transaction Server? Why Is MTS Important for Visual FoxPro Developers? Creating Your First MTS Server Setting Up Security The Basic Features of MTS Just-In-Time Activation Transactions Programming Models Deployment Remote Deployment and Administration Security Shared Property Manager MTS Support for Internet Information Server Automating MTS Administration Tips and TricksExpand table

    Click to copy the sample files associated with this technical article.

    Introduction

    No doubt you’ve heard all about Microsoft Transaction Server (MTS) and how it will make your life easier to develop three-tier applications. This article offers a good primer on using Visual FoxPro 6.0 with MTS. We cover the basics of using MTS and then extend it to using with Visual FoxPro Component Object Model (COM) Components. This document is intended to be used with the Microsoft PowerPoint® slide show included with the Visual FoxPro sample files.

    MTS is a great environment for working with three-tier development. However, one should realize that it is simply not just a matter of dropping your Visual FoxPro servers into an MTS package and expecting miracles. While it is true that much of the work is already done for you, nothing comes for free. Performance and scalability are critical factors that require well-thought-out designs. Good MTS applications are designed with MTS in mind from the start!

    This article assumes that you have MTS already installed. It is available in the Microsoft Windows NT® version 4.0 Option Pack, available from the Microsoft Web site at https://www.microsoft.com/windows/downloads/default.asp.

    In addition, you should familiarize yourself with the basics of MTS. Information is available in the Help files provided with MTS when you install the Windows NT 4.0 Option Pack.

    What Is Microsoft Transaction Server?

    MTS is a component-based transaction processing system for building, deploying, and administering robust Internet and intranet server applications. In addition, MTS allows you to deploy and administer your MTS server applications with a rich graphical tool (MTS Explorer). MTS provides the following features:

    • The MTS run-time environment.
    • The MTS Explorer, a graphical user interface for deploying and managing application components.
    • Application programming interfaces (APIs) and resource dispensers for making applications scalable and robust. Resource dispensers are services that manage nondurable shared state on behalf of the application components within a process.

    The MTS programming model provides a framework for developing components that encapsulate business logic. The MTS run-time environment is a middle-tier platform for running these components. You can use the MTS Explorer to register and manage components executing in the MTS run-time environment.

    The three-tier programming model provides an opportunity for developers and administrators to move beyond the constraints of two-tier client/server applications. You have more flexibility for deploying and managing three-tier applications because:

    • The three-tier model emphasizes a logical architecture for applications, rather than a physical one. Any service may invoke any other service and may reside anywhere.
    • These applications are distributed, which means you can run the right components in the right places, benefiting users and optimizing use of network and computer resources.

    Why Is MTS Important for Visual FoxPro Developers?

    Microsoft is investing a great amount of resources in three-tier development because of a multitude of benefits derived from this architecture. As shown in Figure, Tier 2, the so-called “middle tier,” represents the layer where much of the Application Services/Business Logic is stored. Visual FoxPro COM components are ideally suited for this architecture and will play a key role in this tier for many years to come. This middle tier is also where MTS lives.

    Figure 1. Web-enabled three-tier architecture

    Future applications will consist of Web based front ends using a combination of HTML/XML. While Visual FoxPro data can be used as your database of choice for Tier 3, your applications should be written to communicate to a generic back end. This should be a test of your application’s extensibility. “How easy is it to swap back ends—let’s say Visual FoxPro database to Microsoft SQL Server™?” There are several options, including Open Database Connectivity (ODBC) and ActiveX® Data Objects (ADO), which provide generic interfaces to data. Remember, your application should be written knowing that any or all of the three tiers can be swapped out independent of each other.

    So why is MTS great for Visual FoxPro developers? It should be clear now that the ability to swap out tier components at will makes for a great reusability story. Microsoft has a concept called total cost of ownership (TCO), which means the collective cost of providing and maintaining corporate Information Services. The three-tier model goes a long way toward reducing TCO.

    Updating the Presentation layer is very easy because it merely involves one having to refresh his/her browser. Windows front ends consisting of Visual FoxPro/Visual Basic® forms offer more flexibility in user interface, but updating 150 sites can be time-consuming. In addition, one should expect improved UI options available in HTML.

    The back-end data is usually the tier that changes the least. Having data managed centrally also reduces costs. Remember that data can be distributed and still managed from one location. It doesn’t have to be stored centrally to be managed centrally.

    Finally, we get to Visual FoxPro’s role in the middle tier. Middle-tier components tend to change most often because they represent business rules, which change as the needs of the business changes. Traditional client/server and monolithic applications would often combine the first two layers into one. This was very inefficient because of the distribution costs in updating sites. Today, with browsers, much of this distribution problem goes away. However, business rules are often complex and can contain sensitive/secure information, so it’s not always wise to send these rules back with the HTML to a Web browser. In addition, it can impede performance.

    So, we end up with a dilemma. We want to limit the amount of information sent back to the client, but we also want to minimize the number of back and forth trips between client and server, because bandwidth is also a big consideration (more so with the Internet versus an intranet). The best solution is one involving a so-called “Smart Client.” Traditionally, the Web browser is thought of as an unintelligent client whose job is to merely display an entire static Web page. Each time something on the page changes, we need to refresh the entire Web page. With dynamic HTML (DHTML), you no longer need to do this. Only parts of the Web page affected need updating. In addition, some of the business rules can (and should) reside on the client, thus reducing round trips to the server. For example, you may want to have your client have simple data validation rules, such as one to ensure a value is not negative. It would be more efficient to perform these sorts of checks on the client. Most of the rules, especially sensitive ones, will exist on the server away from client eyes. It is also important to realize, however, that client-side business rules are subject to change almost as frequently as those on the server. The ATSWeb application (available at https://msdn.microsoft.com/vfoxpro/ats_alpha/default.htm) offers a great example of business rules being applied to both client and server.

    MTS provides an environment for hosting your Visual FoxPro middle-tier objects because it handles many of the common tasks, including resource and thread management, security, deployment, application robustness, and transactions. This leaves you, the developer, with only the responsibility of providing business logic specific to your application.

    Creating Your First MTS Server

    Let’s jump right in and create an MTS server, because it’s very simple if you already know how to create a Visual FoxPro COM component.

    Creating a Visual FoxPro COM Component

    1. Create a new project file called test1.pjx
    2. Create a new program file (PRG) called test1.prg
    3. Add the following code to this program:DEFINE CLASS server1 AS custom OLEPUBLIC PROCEDURE hello RETURN “Hello World” ENDPROC ENDDEFINE
    4. Build the server as a DLL (for example, test1.dll). All MTS components must be created as in-process DLL servers. You now have a server that can be tested directly in Visual FoxPro:x=create(“test1.server1”) ? x.hello()

    Adding the Visual FoxPro COM Component to an MTS Package

    A package is a collection of components that run in the same process. Packages define the boundaries for a server process running on a server computer. For example, if you group a Sales component and a Purchasing component in two different packages, these two components will run in separate processes with process isolation. Therefore, if one of the server processes terminates unexpectedly (for instance, because of an application fatal error), the other package can continue to execute in its separate process.

    This section describes the task of installing the Visual FoxPro server into the MTS environment.

    1. Launch MTS Explorer.
    2. In the left pane, navigate to the Computers item and select My Computer. You are now looking at the MTS environment.
    3. Click the Packages Installed node to view all default packages installed by MTS. You can think of a Package as a set of components that perform related application functions. For example, an Inventory package might consist of two DLLs, each performing a task related to checking product inventory for a customer order.
    4. Let’s create a new package now. Select the Action -> New -> Package menu item.
    5. Click the Create an empty package button. Type in a name for your new package (for example, Foxtest1).
    6. Click the Next button, and then click the Finish button. You should now see your new package added under the Packages Installed node.
    7. Click your new package node (for example, Foxtest1). You should now see two items. The Components folder is where you add new components such as the Visual FoxPro component you just created. The Roles folder is where you set up groups of users (roles) who all share similar access privileges (security). You do not need to add anything to the Roles folder in order to use your Visual FoxPro component with MTS.
    8. Click the Components folder and select the Action -> New -> Component menu item.
    9. Click the Install new component(s) button. This will bring up the Install Components dialog box. Click the Add files button and go to the location where you created your Visual FoxPro server (for example, test1.dll). Select both the .dll and .tlb files. The .tlb file is the type library file containing properties and methods of your server. After selecting these two files, you should see your OLEPUBLIC component listed in the lower panel. Click Finish and you should see your server added to this folder.
    10. At this point, your package is complete and ready to go. Later, we will talk about setting Transaction support. This can be done from the Properties dialog box of your server.

    Accessing Your Component

    You can now test your new MTS packaged component using a command similar to the one used to test Visual FoxPro after the DLL server was first created.

    x=create("test1.server1")
    ? x.hello()
    

    That’s all you need to do! If you go back into the MTS Explorer, you should see the component represented with a spinning icon. Click the Status View to see details about the state of the object.

    Figure 2. New component viewed in MTS Explorer

    If you release the object (RELEASE x), MTS releases its reference.

    Going Forward

    We’ve just discussed the basics of installing your Visual FoxPro server in MTS. Essentially, all we did was wrap the Visual FoxPro component inside an MTS process that manages security, transaction state, fault tolerance, and other common server responsibilities. All Visual FoxPro servers used with MTS are registered this way. The remainder of the article discusses how to take advantage of MTS-specific features such as security and transactions. You can write code in your components that talk directly to the MTS run-time environment. In addition, the above process can be entirely automated, because MTS exposes an administrative Automation interface.

    Setting Up Security

    So why are we starting out so early with security? Well, sooner or later, you’re going to fiddle with some sort of security switch and suddenly that MTS application of yours will no longer work. It’s important that you follow these instructions and refer to them later when you decide to add security to your applications.

    Note   MTS 2.0 security setup is described in the Readme document. If you have MTS installed on Microsoft Windows® 95, you can skip this section.

    Setting System Package Identity

    Before you do anything in MTS, it is a good idea to configure the system package for administrating security. When installing MTS, set the system package identity before creating any new packages as follows:

    1. Create a new local Windows NT group named “MTS Administrators” and a new local user named “MTS Administrator.”
    2. Add the “MTS Administrator” user to the “MTS Administrators” and “Administrators” groups.
    3. Set the identity of the system package to “MTS Administrator.” If this does not work, try setting this to the Administrator user.

    Note   You cannot set a package’s identity to a group.

    1. Shut down the system package so that it will be restarted with the new identity. You can do this by right-clicking the My Computer icon in MTS Explorer and selecting Shut Down Server Processes.

    Adding Security for MTS Packages

    You first need to determine whether you want all or just a few components in your Package to have security. Right-click the Package and select Properties. Next, click the Security tab. Then check the Enable authorization checking check box. To enable or disable security at a component level, right-click a component and display the Properties dialog box.

    If this is all you do, an “Access is denied” error message is generated when you try to access your component. You MUST associate a valid role with any component marked for security!

    Right-click the package’s Roles folder and select New Role. Type in a functional role such as Managers, Accountants, and so on.

    The new role is added as a subfolder. Right-click this folder to Add New User (you will get a dialog box to Add Users and Groups to Role). Select the user(s) that you want to add to your role. To finish, select the Role Membership folder under each component that is marked for security and add the new role created in step 3 by right-clicking the folder and selecting New Role.

    Note   You may still experience the “Access is denied” error message when running your components. There are a couple of possible solutions:

    • Sometimes adding a Group to a role does not work (step 3). You might try adding individual users instead.
    • The user rights for that user are not properly set. Make sure the user account for the identities of the system package and other MTS packages have the Windows NT “Log on as a service” user right. You can verify this by using the Windows NT User Manager:
    1. From the Policies menu, select User Rights.
    2. Click Show Advanced User Rights.

    Tips for Visual FoxPro Users

    Much of the security administration can easily be handled by Automation using the MTS Admin objects. You can set up Security administration in the AfterBuild event of a ProjectHook class you have tied to the project that generates your MTS COM DLL server. See the section “Using Visual FoxPro 6.0 Project Hooks” for examples.

    The Basic Features of MTS

    Before we jump right into using Visual FoxPro with MTS, let’s review some basic concepts that you need to know in order to make effective use of the MTS environment. For more detailed information, see MTS Help.

    Activity

    An activity is a collection of MTS objects that has a single distributed thread of logical execution. Each MTS object belongs to a single activity. This is a basic concept that describes how the middle-tier functions when confined to the MTS environment. In an MTS package, multiple clients can access objects, but only one object per client is running at a time on a single thread.

    Context

    Context is state that is implicitly associated with a given MTS object. Context contains information about the object’s execution environment, such as the identity of the object’s creator and, optionally, the transaction encompassing the work of the object. The MTS run-time environment manages a context for each object.

    As a developer, think of every Visual FoxPro object that is registered in an MTS package as having an associated Context object that is created every time you instantiate the Visual FoxPro object. So, each time you issue a CreateObject command, two objects are created—your server and its associated Context. In fact, you can return an object reference to this Context object directly in your code, as in the following example:

    #DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
    LOCAL oMTX,oContext
    oMtx = CREATEOBJECT(MTX_CLASS)
    oContext = oMtx.GetObjectContext()
    

    The Context object has the following properties and methods.Expand table

    CountCreateInstanceDisableCommit
    EnableCommitIsCallerInRoleIsInTransaction
    IsSecurityEnabledItemSecurity
    SetAbortSetComplete 

    As you can see, the properties, events, and methods (PEMs) are used to access information related to the object transaction and security context (see MTS Help for more details on specific syntax for these PEMs). It is important to understand that the Context state is inherited. An object in a package called from another object in the same package will inherit the state of its caller. Because Context is confined within the same process, state, such as security, is trusted. No object in a package needs to explicitly provide its own security. When your object is released, so is its Context.

    Package

    Packages, as we just described, are the building blocks of MTS. Think of them as mini applications—a set of components that perform related application functions. All components in a package run in the same MTS process.

    Remember, “Good MTS applications are designed with MTS in mind from the start.” You should design your Package contents with your entire application in mind. Each package runs in its own process, so try to design packages that don’t attempt to do more than they absolutely need to. There are performance advantages to maintaining many components within in a single package, but there may also be security constraints (roles) that dictate a different architecture.

    Packages are also the primary means of deployment. The MTS environment allows one to export the contents of a Package to a nice distributable setup (both client and server). We’ll discuss this in the “Deployment” section.

    Role

    A role is a symbolic name that defines a class of users for a set of components. Each role defines which users are allowed to invoke interfaces on a component. A role is the primary mechanism to enforce security. Role-based security is handled at the component level. It’s possible that this may be at the method level in a future version of MTS. Security cannot be enforced on the Windows 95 version of MTS.

    Roles are stored at the package level. Each component in a package can belong to one of more of the defined roles. For example, an Inventory package might contain a Visual FoxPro server whose responsibility is to handle inventory. There are two roles defined in this package: Managers and Clerks. These two roles are simply collections of Windows NT users/groups with a collective name that you provide. Your server is coded so that Clerks can access inventory data for normal order entries and reporting. Managers have additional power in that they can override inventory levels to make adjustments (for example, quarterly product shrinkage estimates).

    You can set up security so that it is automatically handled (for instance, users not in roles are given “Access is denied” error message), or you can manage it programmatically through code. The Context object’s IsCallerInRole method is ideal for this.

    Resource Dispensers

    A resource dispenser manages nondurable shared state on behalf of the application components within a process. Resource dispensers are similar to resource managers, but without the guarantee of durability. MTS provides two resource dispensers:

    • The ODBC resource dispenser
    • The Shared Property Manager

    Resources are shared within the same process—same process = same package. In the section “Shared Property Manager,” we discuss programmatically accessing shared properties. This is a really cool thing for Visual FoxPro developers because it allows multiple instances of objects to share state information. For example, you could have a counter that tracks the last ID number used by a database.

    ODBC resource dispenser

    The ODBC resource dispenser manages pools of database connections for MTS components that use the standard ODBC interfaces, allocating connections to objects quickly and efficiently. Connections are automatically enlisted on an object’s transactions and the resource dispenser can automatically reclaim and reuse connections. The ODBC 3.0 Driver Manager is the ODBC resource dispenser; the Driver Manager DLL is installed with MTS.

    Shared Property Manager

    The Shared Property Manager provides synchronized access to application-defined, process-wide properties (variables). For example, you can use it to maintain a Web page hit counter or to maintain the shared state for a multiuser game.

    Resource Managers

    A resource manager is a system service that manages durable data. Server applications use resource managers to maintain the durable state of the application, such as the record of inventory on hand, pending orders, and accounts receivable. Resource managers work in cooperation with the Microsoft Distributed Transaction Coordinator (MS DTC) to guarantee atomicity and isolation to an application. MTS supports resource managers, such as Microsoft SQL Server version 6.5, that implement the OLE Transactions protocol.

    The MS DTC is a system service that coordinates transactions. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers. MS DTC was first released as part of SQL Server 6.5 and is included in MTS, providing a low-level infrastructure for transactions. MS DTC implements a two-phase commit protocol to ensure that the transaction outcome (either commit or abort) is consistent across all resource managers involved in a transaction. MS DTC ensures atomicity, regardless of failures.

    You might be asking if Visual FoxPro is a resource manager, because it has its own native database. Unfortunately, the answer is no. Visual FoxPro transactions are native to Visual FoxPro and do not go through the MS DTC. Therefore, automatic transaction support within MTS is not supported for Visual FoxPro data. You cannot use the Context object’s SetAbort method to abort a transaction if the data is stored in Visual FoxPro databases/tables. The database must either support OLE Transactions (SQL Server) or be XA-compliant (Oracle).

    Base Clients

    A base client is simply a client that runs outside of the MTS run-time environment, but instantiates MTS objects. In a three-tier architecture, a base client is typically the presentation layer, such as an application form or Web page. The base client neither knows nor needs to know that MTS is used in the middle tier. It merely creates an instance of an object that exists in an MTS package and awaits a response. The following table describes some of the differences between a base client and an MTS component, such as a Visual FoxPro DLL server.Expand table

    Base clientMTS component
    Can be EXEs, DLLs.Must be in-process DLL.
    MTS does not manage its process.Manages server processes that host MTS component.s
    MTS does not create or manage threads used by application.Creates and manages threads.
    Does not have implicit Context object.Each MTS object has own Context object.
    Cannot use Resource Dispensers.Can use Resource Dispensers.

    Just-In-Time Activation

    Just-in-Time (JIT) activation is the ability to activate an MTS object only as needed for executing requests from a client. Most Visual FoxPro developers are familiar with object instantiation, as in the following code:

    myObject = CreateObject("myclass")
    myObject.myMethod()
    myObject.myProperty = 123
    RELEASE myObject
    

    A “stateful” object created by this code retains state during the lifetime of the object (until it is released). This means that property values (such as myProperty) are retained between statement execution. When the object is finally released, all object references and state are released.

    There is overhead with creating objects from your Visual FoxPro components. Each time you instantiate an object, Visual FoxPro needs to allocate a certain amount of memory. In addition, the first time you create an object, Visual FoxPro takes a little extra time to load its run-time libraries. When the last instance is released, the entire Visual FoxPro run time is also released.

    JIT activation addresses many of these memory issues that affect performance. The first thing JIT does is cache the server’s run-time libraries in memory, even though no outstanding object references exist. The first time you instantiate a Visual FoxPro server that’s in an MTS package, the Visual FoxPro run time loads the address space of the MTS process. When you release the object, MTS still keeps the libraries in memory for a specified amount of time. You can change this setting in the package’s property sheet (default = 3 minutes). This saves having to reload the run time when the object count hits 0.

    The main thing that JIT activation offers is ability to transform your object from “stateful” to “stateless” mode. In the preceding example, you can interpret a “stateless” object as one having the initial default settings. So, in the example, the value of myProperty would be reset to its original setting. A stateless object is managed by MTS and is very lightweight, so it consumes much less memory. The only thing keeping the stateless object alive is the object reference held onto by the client. Internally, MTS recycles threads consumed by stateful objects when they go stateless. When a method is invoked on that object, it then becomes stateful again on a thread that could be different from the one originally created on.

    Putting your objects into a stateless mode is handled easily by the Context object. The following code illustrates putting an object in a stateless mode:

    #DEFINE MTX_CLASS   "MTXAS.APPSERVER.1"
    LOCAL oMTX,oContext
    oMtx = CREATEOBJECT(MTX_CLASS)
    oContext = oMtx.GetObjectContext()
    oContext.SetComplete()
    

    This code is actually called from within a method of your Visual FoxPro server. You can see if your object is stateless by viewing the status of your component in the MTS Explorer. A stateless object appears in the Objects column, but not in the Activated or In Call columns.

    Use the SetComplete method to put the object in a stateless mode. Use SetComplete for committing transactions (as we discuss in the next section, “Transactions”). You can also use SetAbort to make an object stateless.

    Again, when you change an object to stateless, all property settings revert to their original defaults. When you invoke a method (or property set/get) on this stateless object, the object is activated (goes stateful) and the object’s INIT event is fired. When you call SetComplete, the object DESTROY event is fired.

    Note   Any state that exists on the object is lost when the object is deactivated (SetComplete). If you need to save state, you should either persist information to a database or use the MTS Shared Property Manager.

    Because your object’s INIT is called whenever your object goes from Stateless to Stateful, you should try to minimize the amount of code in this event.

    Here is a simple scenario showing interaction between client and MTS server.

    Visual FoxPro server code:

    DEFINE CLASS mts2 AS Custom OLEPUBLIC
       MyColor = "Green"
       PROCEDURE InUsa (tcCustID)
          LOCAL llInUSA,oMTX,oContext
          oMtx = CreateObject("MTXAS.APPSERVER.1")
          oContext = oMtx.GetObjectContext()
          llInUSA = .F.
          USE CUSTOMER AGAIN SHARED
          LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
          IF FOUND()
             llInUSA = (ATC("USA",country)#0)
          ENDIF
          oContext.SetComplete()
          RETURN llInUSA
       ENDPROC
    ENDDEFINE
    

    Base client executes following code:

    LOCAL oCust,cCust,lUsa
    oCust = CreateObject("vfp_mts.mts2")
    ? oCust.MyColor
    Green
    oCust.MyColor = "Red"
    ? oCust.MyColor
    Red
    cCust = "JONES"
    lUsa = oCust.InUsa(cCust)   && object goes stateless (deactivated)
    ? oCust.MyColor      && object is activated (stateful)
    Green
    RELEASE oCust         && object is fully released
    

    Notice in the preceding example how the state of oCust is lost after the InUsa method is called. The MyColor property no longer returns Red, but is instead reset to its original value of Green.

    Transactions

    If you have used Visual FoxPro at all, you are probably aware that Visual FoxPro supports transactions. Changes to your data can be committed or rolled back. Though transactions are critical to MTS, don’t be misled by the name; there is a lot more to it than just transactions. However, the ability to have MTS automatically handle transactions between distributed objects is quite powerful. Transactions are often discussed in terms of the ACID acronym:

    • Atomicity—ensures that either the entire transaction commits or nothing commits.
    • Consistency—a transaction is a correct transformation of the system state.
    • Isolation—protects concurrent transactions from seeing each other’s partial and uncommitted results.
    • Durability—committed updates to managed resources can survive failures.

    As just mentioned, MTS transaction support is not compatible with Visual FoxPro data. It only works with databases supporting OLE transaction or XA protocols. Both SQL Server and Oracle data can be used with MTS in transactional fashion.

    You should understand what we mean by a transaction and to what extent things are either committed or rolled back. Consider the following scenario (all done within confines of two components in a single MTS package):

    1. Component A adds a new customer record to the Customer table in SQL Server.
    2. Component A writes out new record to a Visual FoxPro database (audit log).
    3. Component A sends e-mail notification of new customer to some manager.
    4. Component A calls Component B.
    5. Component B edits the Orders table with a new order in SQL Server.
    6. Component B writes out text log file of activity.
    7. Component B completes activity by committing the transaction (SetComplete).
    8. Component A discovers bad credit history with customer and aborts transaction (SetAbort).

    When Component B commits in step 7, not a whole lot happens because MTS manages the entire Context within the package in a distributed fashion. Component B actually inherits transaction state from Component A, so it cannot really fully commit the transaction. The real transaction terminates in step 8 when the last object with transaction state aborts. At this point, changes made to both Customer and Orders tables are rolled back because these tables are in SQL Server. Unfortunately, the Visual FoxPro table update, e-mail notification, and text log file activities are not rolled back. When a transaction is aborted/committed, only data managed through the MS DTC is affected. There is no event that is magically triggered. (Check out the MTS SDK for ideas on using Spy).

    Remember, good MTS apps are written with MTS in mind from the start. Managing transactions is very important, and while much of it is handled automatically, you will need to provide a fair amount of code to effectively manage all the resources being utilized in a transaction setting.

    Transaction support is set at the component level, but transactions can span multiple packages. You can set this option in the MTS Explorer from the component’s Property Sheet (see MTS Help for details on the various options). Again, the object’s Context manages and passes on transaction state for a given component. If the transaction setting of a component is marked as “Requires a transaction,” a transaction is always associated with the component. If another object that calls this component already has a transaction in effect, no new transaction is created. The component merely inherits the current one. A new transaction is only created if one does not already exist in the context.

    Figure 3. Setting Transaction support

    Let’s return a minute to the SetComplete and SetAbort methods. These methods actually serve two purposes. From their names, they imply functionality related to transactions. However, as already discussed, they also serve to deactivate objects (make them stateless). In fact, these methods can be used simply for JIT activation without any concern for transactional support. Again, SetComplete releases valuable resources/memory used by MTS to allow for improved scalability. The Context object also includes several other methods useful for transactions: EnableCommitDisableCommit, and IsInTransaction. The following example shows how to handle transactions in Visual FoxPro:

    LPARAMETER tcCustID
    LOCAL lFound,oMTX,oContext
    oMtx = CreateObject("MTXAS.APPSERVER.1")
    oContext = oMtx.GetObjectContext()
    USE CUSTOMER AGAIN SHARED
    LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
    lFound = FOUND()
    IF FOUND()
    oContext.SetComplete()
    ELSE
    oContext.SetAbort()
    ENDIF
    RETURN lFound
    

    In this scenario, we assume that another component already performed an update on another table (for example, Orders). If the customer ID in the preceding code was not found, the entire transaction would be rolled back.

    You’re probably wondering how transactions work in the code, which clearly appears to be against Visual FoxPro data. Actually, this example is using Remote Views against SQL Server data. Again, Visual FoxPro tables do not support OLE transactions, so you will not get MTS transaction support if you use DBF tables. However, data updates either to Remote Views or by SQL pass-through work just fine.

    **Tip   **Make sure that your connection to a remote data source is made without any login dialog box. If you are using a connection stored in a DBC, ensure that the Display ODBC logins prompt is set to Never. For access to remote data through SQL pass-through commands, you can use the SQLSetProp function:

     SQLSETPROP(0, 'DispLogin', 3)
    

    Programming Models

    MTS supports two programming models. The TransactionContext model is intended primarily for backward compatibility. It essentially lets the base client control the transaction. The assumption is that the COM component has no MTS awareness (that is, the component was written before MTS was available). The second model is called the ObjectContext model and assumes the COM component inside the MTS package has MTS smarts and is aware of its Context object.

    TransactionContext

    We do not recommend using this model for new three-tier applications, because it has limited access to the full capabilities of MTS. It merely offers a way to provide some transaction support to applications whose middle-tier components were developed without MTS in mind. The burden of transaction handling rests on the base client. With this model, the base client is likely to be a smart client that has scripting capabilities (for example, an application form). The base client is less likely to be a Web page, and it always runs outside of the MTS run-time environment.

    The following code snippet in a Visual FoxPro form (base client) shows this model in use. The middle-tier component is a Visual FoxPro server whose ProgID is “vfp_mts.mts1”. The assumption here is that this server knows nothing about MTS, thus requiring the base client to perform all transaction handling:

    #DEFINE TRANS_CLASS   "TxCtx.TransactionContext"
    THIS.oContext = CreateObject(TRANS_CLASS)
    LOCAL loCust
    loCust = THISFORM.oContext.CreateInstance("vfp_mts.mts1")
    RETURN loCust.lnUSA
    

    The code in the middle tier simply does a lookup in a SQL Server table for a customer’s home country. If the record was actually changed, the base client would have the capability to actually commit or roll back the transaction. The TransactionContext object only supports three methods: CreateInstanceCommit, and Abort.

    ObjectContext

    The ObjectContext model is the only model you should consider for new MTS application development. It relies on component awareness of MTS, but this should be your goal so that you can optimize performance and take advantage of MTS-specific features.

    Unlike the TransactionContext object, which uses the following PROGID:

    #DEFINE TRANS_CLASS   "TxCtx.TransactionContext"
    

    the ObjectContext object can be accessed using the following code:

    #DEFINE MTX_CLASS   "Mtxas.AppServer.1"
    

    The ObjectContext object, which can be referenced in your Visual FoxPro code, as shown here:

    LOCAL oMTX,oContext
    oMtx = CreateObject("MTXAS.APPSERVER.1")
    oContext = oMtx.GetObjectContext()
    

    contains the following properties, events, and methods (PEMs).Expand table

    PEMDescription
    CountReturns the number of Context object properties.
    CreateInstanceInstantiates another MTS object.
    DisableCommitDeclares that the object hasn’t finished its work and that its transactional updates are in an inconsistent state. The object retains its state across method calls, and any attempts to commit the transaction before the object calls EnableCommit or SetComplete will result in the transaction being aborted.
    EnableCommitDeclares that the object’s work isn’t necessarily finished, but its transactional updates are in a consistent state. This method allows the transaction to be committed, but the object retains its state across method calls until it calls SetComplete or SetAbort, or until the transaction is completed.
    IsCallerInRoleIndicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
    IsInTransactionIndicates whether the object is executing within a transaction.
    IsSecurityEnabledIndicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
    ItemReturns a Context object property.
    SecurityReturns a reference to an object’s SecurityProperty object.
    SetAbortDeclares that the object has completed its work and can be deactivated on returning from the currently executing method, but that its transactional updates are in an inconsistent state or that an unrecoverable error occurred. This means that the transaction in which the object was executing must be aborted. If any object executing within a transaction returns to its client after calling SetAbort, the entire transaction is doomed to abort.
    SetCompleteDeclares that the object has completed its work and can be deactivated on returning from the currently executing method. For objects that are executing within the scope of a transaction, it also indicates that the object’s transactional updates can be committed. When an object that is the root of a transaction calls SetComplete, MTS attempts to commit the transaction on return from the current method.

    Deployment

    Microsoft Transaction Server offers excellent tools for deploying both client- and server-side setups. Setups are made at the package level, so you should include all components for your application in a particular package. The deployment package contains all the distributed COM (DCOM) configuration settings you need, so you don’t have to fuss with the messy DCOM Configuration dialog box.

    To create a setup

    1. Click the package that you want to create setup.
    2. Select Export… from the Action menu. The Export dialog box is displayed.

    Figure 4. Exporting a package

    **Important   **The directions in the Export dialog box are not very clear. You should not simply type in a path as specified. If you do, the Export routine creates a file with a .pak extension in the folder location you specify. Instead, you should always type a full path and file name for the .pak file, as shown in Figure 4.

    You can also use the scriptable administration objects to automate deployment and distribution of your MTS packages. See the section “Remote Deployment and Administration” to follow for more details.

    The output of the Export operation consists of two setups:

    Server Setup

    This setup, which is placed in the folder specified in the Export dialog box, contains the .pak file and all COM DLL servers used by the package.

    Note   With Visual FoxPro servers, you will also have .tlb (type library) files included. You can install this package by selecting Install from the Package Wizard in MTS Explorer.

    Figure 5. Installing package from the Package Wizard

    Client Setup

    The Export process creates a separate subfolder named “clients” in the folder specified in the Export Package dialog box. The Clients folder contains a single .exe file that a user can double-click to run.

    The Client setup merely installs necessary files and registry keys needed by a client to access (remotely through DCOM) your MTS package and its COM servers.

    Remote Deployment and Administration

    The MTS Explorer allows you to manage remote components (those installed on a remote machine). The Remote Components folder contains the components that are registered locally on your local computer to run remotely on another computer. Using the Remote Components folder requires that you have MTS installed on the client machines that you want to configure. If you want to configure remote computers manually using the Explorer, add the components that will be accessed by remote computers to the Remote Components folder.

    Pushing and Pulling

    If both the server and client computer are running MTS, you can distribute a package by “pulling” and “pushing” components between one or more computers. You can “push” components by creating remote component entries on remote computers and “pull” components by adding component entries to your local computer. Once you create the remote component entries, you must add those component entries to your Remote Components folder on your local machine (pull the components).

    Before you deploy and administer packages, set your MTS server up by doing the following:

    • Configure roles and package identity on the system package.
    • Set up computers to administer.

    You must map the System Package Administrator role to the appropriate user in order to safely deploy and manage MTS packages. When MTS is installed, the system package does not have any users mapped to the administrator role. Therefore, security on the system package is disabled, and any user can use the MTS Explorer to modify package configuration on that computer. If you map users to system package roles, MTS will check roles when a user attempts to modify packages in the MTS Explorer.

    Roles

    By default, the system package has an Administrator role and a Reader role. Users mapped to the Administrator role of the system package can use any MTS Explorer function. Users that are mapped to the Reader role can view all objects in the MTS Explorer hierarchy but cannot install, create, change, or delete any objects, shut down server processes, or export packages. If you map your Windows NT domain user name to the System Package Administrator role, you will be able to add, modify, or delete any package in the MTS Explorer. If MTS is installed on a server whose role is a primary or backup domain controller, a user must be a domain administrator in order to manage packages in the MTS Explorer.

    You can also set up new roles for the system package. For example, you can configure a Developer role that allows users to install and run packages, but not delete or export them. The Windows NT user accounts or groups that you map to that role will be able to test installation of packages on that computer without having full administrative privileges over the computer.

    In order to work with a remote computer, you first need to add it to the Computers folder in the MTS Explorer:

    1. Click the Computers folder.
    2. Select New -> Computer from the Action menu.
    3. Enter name of the remote computer.

    Important   You must be mapped to the Administrator role on the remote computer in order to access it from your machine. In addition, you cannot remotely administer MTS on a Windows 95 computer from MTS on a Windows NT server.

    You should now see both My Computer and the new remote computer under the Computers folder. At this point, you can push and pull components between the two machines. Think of the Remote Components folder as its own special package. You are merely adding to it components that exist in one or more packages of remote machines.

    The following example pulls a component from a remote machine to My Computer.

    1. Click the Remote Components folder of My Computer.
    2. Select New-> Remote Component from the Action menu to display the dialog box shown here.

    Figure 6. Adding a component to Remote Components

    In this example, we select (and add) a component called test6.foobar2 from a package called aa on the remote machine calvinh5. This package also has another component (Visual FoxPro OLEPUBLIC class) named test6.foobar, which we do not select. When we click OK, a copy of the DLL and the type library are copied to the local machine (My Computer) and stored in a subfolder of your MTS root location (in this case, c:\ C:\Program Files\Mts\Remote\aa\). In addition, the server is now registered on your machine. Note that while the DLL is copied to your machine, the .dll registered in your registry points to the remote machine.

    If you encounter problems after you click OK, you may not have proper access rights to copy the server components. Ensure that the remote machine is configured with proper access privileges for you. At this point, you can go into Visual FoxPro running on the local machine and access the server:

    oServer = CreateObject("test6.foobar2")
    ? oServer.myeval("SYS(0)")
    

    You use MTS Explorer to view the activated object in the remote machine folder under the package it is registered in. You will not see the object activity in the Remote Components folder. See the “Working with Remote MTS Computers” topic in the MTS Help file for more details.

    Security

    Security in MTS is handled by roles. Roles are established at the package level. Components within that package can set up role memberships. The following MTS Explorer image shows a package called Devcon1, which contains three roles. Only the last two components contain Role Memberships.

    Figure 7. Package with roles

    If you navigate the Roles folder, you can see all Windows NT users or groups assigned to that particular role.

    To create a new role

    1. Click the Roles folder.
    2. Select New-> Role from the Action menu.
    3. Enter a new role name in the dialog box.

    You can add new users/groups to a particular role as follows:

    To add new users or groups

    1. Click the Users folder of the newly added role.
    2. Select New-> User from the Action menu.
    3. Select users/groups from the dialog box.

    MTS handles its security several different ways. The MTS security model consists of declarative security and programmatic security. Developers can build both declarative and programmatic security into their components prior to deploying them on a Windows NT security domain.

    You can administer package security using MTS Explorer. This form of declarative security, which does not require any component programming, is based on standard Windows NT security. This can be done by Package- or Component-level security.

    Declarative Security

    You can manage Declarative security at the package and at the component level through settings available in the Security tab of the Package Properties dialog box.

    Package-level security

    Each package has its own security access authorization, which can be set in the Package Properties dialog box.

    Figure 8. Package properties

    By default, the Security check box is not marked, so you need to check this box to enable security. If you do not enable security for the package, MTS will not check roles for the component. If security is enabled, you must also enable security at the component level in order to have roles checked.

    Component-level security

    Each installed component can also have its own security setting. You set security for a component through the same Enable authorization checking check box on the Property dialog box in MTS Explorer. If you are enabling security at both levels and you do have defined roles, you must include one of the roles in the component’s Role Membership folder. If you do not include a role in the folder, you will get an “Access is denied” error message when you try to access a property or method of the component. Of course, if you do not have any roles, you will get the same error.

    Note   You can still do a CreateObject on the component, but that is all.

    oContext = CreateObject("vfp_mts.mts1")
    oContext.Hello()   && will generate an Access is denied error
    

    To restrict access to a specific component within a package, you must understand how components in the package call one another. If a component is directly called by a base client, MTS checks roles for the component. If one component calls another component in the same package, MTS does not check roles because components within the same package are assumed to “trust” one another.

    When you change the security settings for a particular package or component, you need to shut down server processes before changes can take place. This option is available from the Action menu when Package is selected.

    Programmatic Security

    You can put code in your program to check for specific security access rights. The following three properties and methods from the Context object return information regarding security for that package or component.Expand table

    MethodsDescription
    IsCallerInRoleIndicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
    IsSecurityEnabledIndicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
    SecurityReturns a reference to an object’s SecurityProperty object.

    The following method checks whether the called object is in a particular role. The IsCallerInRole method is useful when the roles are defined, but if your code is generic and doesn’t know the particular roles associated with a component, you must handle this through your error routine.

    PROCEDURE GetRole (tcRole)   
       LOCAL oMTX,oContext,lSecurity,cRole,lHasRole
       IF EMPTY(tcRole)
          RETURN "No Role"
       ENDIF
       oMtx = CREATEOBJECT(MTX_CLASS)
       oContext = oMtx.GetObjectContext()
       IF oContext.IsSecurityEnabled
          THIS.SkipError=.T.
          lHasRole = oContext.IsCallerInRole(tcRole)
          THIS.SkipError=.F.
          DO CASE
          CASE THIS.HadError
             THIS.HadError = .F.
             cRole="Bad Role"
          CASE lHasRole 
             cRole="Yep"
          OTHERWISE
             cRole="Nope"
          ENDCASE
    ELSE
          cRole="No Security"
    ENDIF
       oContext.SetComplete()
       RETURN cRole
    ENDPROC
    

    Advanced users can access the SecurityProperty object to obtain more details on the user for handling security. The Security object offers the following additional methods.Expand table

    MethodDescription
    GetDirectCallerNameRetrieves the user name associated with the external process that called the currently executing method.
    GetDirectCreatorNameRetrieves the user name associated with the external process that directly created the current object.
    GetOriginalCallerNameRetrieves the user name associated with the base process that initiated the call sequence from which the current method was called.
    GetOriginalCreatorNameRetrieves the user name associated with the base process that initiated the activity in which the current object is executing.

    What type of security should you use? Programmatic security offers more power in terms of structuring specific functionality for particular roles. You can use Case statements, as in the previous example, which perform different tasks, depending on the role. Declarative security, on the other hand, can only control access at the component level (not method or lower).

    Changes to Programmatic security, however, require a new build of the component, which may not always be convenient or realistic. Controlling Component-level security for users and roles by using MTS Explorer to turn security on or off gives an administrator greater control. The optimal solution is one with utilizes both declarative and programmatic securities in the most efficient manner.

    Shared Property Manager

    The Shared Property Manager (SPM) MTS resource dispenser allows you to create and share properties across components. Because it is a resource dispenser, all other components in the same package can share information, but information cannot be shared across different packages. For example, if you want to keep a counter to use for generating unique IDs for objects in a package, you could create a Counter property to hold the latest unique ID value. This property would be preserved while the package was active (regardless of object state).

    The SPM also represents an excellent way for an object to preserve its state before being deactivated in a stateless mode (SetComplete). Just-In-Time activation does not affect or reset the state of SPM.

    The following example shows how to use the SPM with Visual FoxPro servers:

    #DEFINE MTX_CLASS        "MTXAS.APPSERVER.1"
    #DEFINE MTX_SHAREDPROPGRPMGR "MTxSpm.SharedPropertyGroupManager.1"
    PROCEDURE GetCount (lReset)
       LOCAL oCount 
       LOCAL oMTX,oContext
       LOCAL nIsolationMode,nReleaseMode,lExists
       oMtx = CREATEOBJECT(MTX_CLASS)
       oContext = oMtx.GetObjectContext()
       oSGM = oContext.CreateInstance(MTX_SHAREDPROPGRPMGR)
       nIsolationMode = 0
       nReleaseMode = 1
       
    * Get group reference in which property is contained
       oSG = oSGM.CreatePropertyGroup("CounterGroup", nIsolationMode,;
    nReleaseMode, @lExists)
       
    * Get object reference to shared property
       oCount = oSG.CreateProperty("nCount", @lExists)
    * check if property already exists otherwise reset
       IF lReset OR !lExists
          oCount.Value = 1
       ELSE
          oCount.Value = oCount.Value + 1
       ENDIF
       RETURN oCount.Value
    ENDPROC
    

    The following settings are available for Isolation and Release modes.

    Isolation mode

    LockSetGet 0 (default)—Locks a property during a Value call, assuring that every get or set operation on a shared property is atomic. This ensures that two clients can’t read or write to the same property at the same time, but doesn’t prevent other clients from concurrently accessing other properties in the same group.

    LockMethod 1—Locks all of the properties in the shared property group for exclusive use by the caller as long as the caller’s current method is executing. This is the appropriate mode to use when there are interdependencies among properties or in cases where a client may have to update a property immediately after reading it before it can be accessed again.

    Release mode

    Standard 0 (default)—When all clients have released their references on the property group, the property group is automatically destroyed.

    Process 1—The property group isn’t destroyed until the process in which it was created has terminated. You must still release all SharedPropertyGroup objects by setting them to Nothing.

    MTS Support for Internet Information Server

    MTS includes several special system packages for use with Microsoft Internet Information Server (IIS). The Windows NT Options Pack 4.0 integrates MTS and IIS more closely. In the future, you can expect even better integration to play a more central role in your Web applications.

    IIS Support

    • Transactional Active Server Pages—You can now run Scripts in Active Server Pages (ASP) within an MTS-managed transaction. This extends the benefits of MTS transaction protection to the entire Web application.
    • Crash Protection for IIS Applications—IIS Web applications can now run within their own MTS package, providing process isolation and crash protection for Web applications.
    • Transactional Events—You can embed commands in scripts on ASP pages, enabling you to customize Web application response based on transaction results.
    • Object Context for IIS Built-In Objects—The MTS object context mechanism, which masks the complexity of tracking user state information from the application developer, now tracks state information managed by IIS built-in objects. This extends the simplicity of the MTS programming model to Web developers.
    • Common Installation and Management—MTS and IIS now share common installation and a common management console, lowering the complexity of deploying and managing business applications on the Web.

    IIS System Packages

    If you use MTS with Internet Information Server version 4.0, the Packages Installed folder contains the following IIS-specific system packages.

    IIS in-process applications

    The IIS In-Process Applications folder contains the components for each Internet Information Server application running in the IIS process. An IIS application can run in the IIS process or in a separate application process. If an IIS application is running in the IIS process, the IIS application will appear as a component in the IIS In-Process Applications folder. If the IIS application is running in an individual application process, the IIS application will appear as a separate package in the MTS Explorer hierarchy.

    IIS utilities

    The IIS Utilities Folder contains the ObjectContext component required to enable transactions in ASP pages. For more information about transactional ASP pages, refer to the Internet Information Server documentation.

    Automating MTS Administration

    Microsoft Transaction Server contains Automation objects that you can use to program administrative and deployment procedures, including:

    • Installing a prebuilt package.
    • Creating a new package and installing components.
    • Enumerating through installed packages to update properties.
    • Enumerating through installed packages to delete a package.
    • Enumerating through installed components to delete a component.
    • Accessing related collection names.
    • Accessing property information.
    • Configuring a role.
    • Exporting a package.
    • Configuring a client to use Remote Components.

    You can use the following Admin objects in your Visual FoxPro code.Expand table

    ObjectDescription
    CatalogThe Catalog object enables you to connect to MTS Catalog and Access collections.
    CatalogObjectThe CatalogObject object allows you to get and set object properties.
    CatalogCollectionUse the CatalogCollection object to enumerate, add, delete, and modify Catalog objects and to access related collections.
    PackageUtilThe PackageUtil object enables installing and exporting a package. Instantiate this object by calling GetUtilInterface on a Packages collection.
    ComponentUtilCall the ComponentUtil object to install a component in a specific collection and import components registered as in-process servers. Create this object by calling GetUtilInterface on a ComponentsInPackage collection.
    RemoteComponentUtilUsing the RemoteComponentUtil object, you can program your application to pull remote components from a package on a remote server. Instantiate this object by calling GetUtilInterface on a RemoteComponents collection.
    RoleAssociationUtilCall methods on the RoleAssociationUtil object to associate roles with a component or interface. Create this object by calling the GetUtilInterface method on a RolesForPackageComponent or RolesForPackageComponentInterface collection.

    In addition, the following collections are also supported.Expand table

    Collection
    LocalComputer
    ComputerList
    Packages
    ComponentsInPackage
    RemoteComponents
    InterfacesForComponent
    InterfacesForRemoteComponent
    RolesForPackageComponent
    RolesForPackageComponentInterface
    MethodsForInterface
    RolesInPackage
    UsersInRole
    ErrorInfo
    PropertyInfo
    RelatedCollectionInfo

    If you want to get a reference to a particular collection, use the GetCollection method. The following example shows, first, getting the collection of packages and, second, getting a collection of all components in the first package:

    #DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oPackages.populate()
    ? oPackages.Count
    oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackages.Item(0).Key)
    oComps.Populate()
    

    Note   The GetCollection method merely returns an object reference to an empty collection. You need to explicitly call the Populate method to fill the collection.

    Collections are case sensitive, as in the following example code:

    oPackages = oCatalog.GetCollection("Localcomputer")   &&fails
    oPackages = oCatalog.GetCollection("LocalComputer")   &&works
    

    Note   Also keep in mind that all MTS collections are zero-based.

    oPackages = oCatalog.GetCollection("LocalComputer")
    oPackages.populate()
    ? oPackages.item[0].name
    

    See MTS Help for more specific language details.

    Visual FoxPro 6.0 is ideally suited for using MTS Automation because of the new Project Manager and Application Builder hooks support.

    Using Visual FoxPro 6.0 Project Hooks

    The MTS samples posted along with this document contain a special Project Hook class designed specially for MTS. This class automatically shuts down and refreshes MTS registered servers contained in that project. One of the issues that developers must consider when coding and testing servers under MTS is repeatedly opening the MTS Explorer to manually shut down processes so that servers can be rebuilt and overwritten. Using a Project Hook nicely automates this process. Here is sample code from the BeforeBuild event, which iterates through the Packages collection shutting-down processes.

    * BeforeBuild event
    LPARAMETERS cOutputName, nBuildAction, lRebuildAll, lShowErrors, lBuildNewGuids
    #DEFINE MTS_CATALOG      "MTSAdmin.Catalog.1"
    #DEFINE   MSG_MTSCHECK_LOC   "Shutting down MTS servers...."
    LOCAL oCatalog,oPackages,oUtil,i,j,oComps
    LOCAL oProject,lnServers,laProgIds,lcSaveExact
    THIS.lBuildNewGuids = lBuildNewGuids
    oProject = _VFP.ActiveProject
    lnServers = oProject.servers.count
    DIMENSION THIS.aServerInfo[1]
    STORE "" TO THIS.aServerInfo
    IF lnServers = 0 OR nBuildAction # 4
       RETURN
    ENDIF
    WAIT WINDOW MSG_MTSCHECK_LOC NOWAIT
    DIMENSION laProgIds[lnServers,3]
    FOR i = 1 TO lnServers
       laProgIds[m.i,1] = oProject.servers[m.i].progID
       laProgIds[m.i,2] = oProject.servers[m.i].CLSID
       laProgIds[m.i,3] = THIS.GetLocalServer(laProgIds[m.i,2])
    ENDFOR
    ACOPY(laProgIds,THIS.aServerInfo)
    * Shutdown servers
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oUtil = oPackages.GetUtilInterface
    oPackages.Populate()
    lcSaveExact = SET("EXACT")
    SET EXACT ON
    FOR i = 0 TO oPackages.Count - 1
       oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackages.Item(m.i).Key)
       oComps.Populate()
       FOR j = 0 TO oComps.Count-1
    IF ASCAN(laProgIds,oComps.Item(m.j).Value("ProgID")) # 0
    oUtil.ShutdownPackage(oPackages.Item(m.i).Value("ID"))
    EXIT
    ENDIF
       ENDFOR
    ENDFOR
    WAIT CLEAR
    SET EXACT &lcSaveExact
    * User is building new GUIDs, so packages 
    * need to be reinstalled manually
    IF lBuildNewGuids
       RETURN
    ENDIF
    

    This is only one of the many possibilities provided by a Visual FoxPro Project Hook. The MTS Admin objects can save a great deal of time you normally would spend manually setting options in the MTS Explorer.

    Using Visual FoxPro 6.0 Application Builders

    As with the Project Hooks, you might also want to create an Application (Project) Builder that handles registration of Visual FoxPro Servers in MTS packages. The Visual FoxPro MTS samples include such a builder. (See the Readme file in the mtsvfpsample sample application for more details on setup and usage of these files.)

    This Builder simply enumerates through all the servers in your Visual FoxPro project and all the available MTS packages. You can then select (or create) a particular package and registered server to install in that package. Additionally, you can set the Transaction property for each component. The Visual FoxPro code called when the user clicks OK is as follows:

    #DEFINE   MTS_CATALOG      "MTSAdmin.Catalog.1"
    #DEFINE   ERR_NOACTION_LOC   "No action taken."
    LOCAL oCatalog,oPackages,oUtil,i,j,oComps,nPoslcPackage
    LOCAL lPackageExists,oCompRef
    LOCAL oProject,lnServers,laProgIds,lcSaveExact,oPackageRef,lctrans
    lcPackage = ALLTRIM(THIS.cboPackages.DisplayValue)
    lPackageExists = .f.
    SELECT mtssvrs
    LOCATE FOR include
    IF !FOUND() OR EMPTY(lcPackage)
       MESSAGEBOX(ERR_NOACTION_LOC)
       RETURN
    ENDIF
    THIS.Hide
    oCatalog = CreateObject(MTS_CATALOG)
    oPackages = oCatalog.GetCollection("Packages")
    oPackages.Populate()
    FOR i = 0 TO oPackages.Count-1
       IF UPPER(oPackages.Item(m.i).Name) == UPPER(lcPackage)
          oPackageRef = oPackages.Item(m.i)
          lPackageExists=.T.
          EXIT
       ENDIF
    ENDFOR
    IF !lPackageExists   &&creating new package
       oPackageRef = oPackages.Add
       oPackageRef.Value("Name") = lcPackage
       oPackages.SaveChanges
    ENDIF
    oComps = oPackages.GetCollection("ComponentsInPackage",;
    oPackageRef.Key)
    oUtil = oComps.GetUtilInterface
    SCAN FOR include
       oUtil.ImportComponentByName(ALLTRIM(progid))
    ENDSCAN
    oPackages.SaveChanges()
    oComps.Populate()
    SCAN FOR include
       DO CASE
       CASE trans = 1
          lctrans = "Supported"
       CASE trans = 2
          lctrans = "Required"
       CASE trans = 3
          lctrans = "Requires New"
       OTHERWISE
          lctrans = "Not Supported"         
       ENDCASE
       FOR j = 0 TO oComps.Count-1
          IF oComps.Item(m.j).Value("ProgID")=ALLTRIM(progid)
             oCompRef = oComps.Item(m.j)
             oCompRef.Value("Transaction") = lctrans
             oCompRef.Value("SecurityEnabled") = ;
    IIF(THIS.chkSecurity.Value,"Y","N")
          ENDIF
       ENDFOR
    ENDSCAN
    oComps.SaveChanges()
    oPackages.SaveChanges()
    

    Tips and Tricks

    Hopefully, this article offers enough insight into creating Visual FoxPro components that work well with your three-tier MTS applications. Here are a few final tips to consider:

    • Design your components with MTS in mind from the start.
    • Components must be in-process DLLs. Do not use Visual FoxPro EXE servers.
    • When adding Visual FoxPro components, make sure to select both .dll and .tlb files.
    • In the Project Info dialog box of Visual FoxPro DLL servers, set Instancing to MultiUse.
    • Don’t be afraid to mix with other components (for example, Visual Basic servers).
    • You must have DTC running for transaction support.
    • Call SetComplete regardless of whether you’re using transactions, because it places objects in stateless mode.
    • Your MTS object has an associated Context object. Do not place this code in the base client.
    • Connections must have DispLogin set to Never; for SQL pass-through, use SQLSetProp(0).
    • Minimize the number of PEMs on an object (protect your PEMs).
    • Because of page locking issues, limit the length of time you leave SQL Server 6.5 transactions uncommitted.
    • To use security, you must have a valid role associated with the component.
    • Avoid using CreateInstance on non-MTS components.
    • Do not pass object references of the Context object outside of the object itself.
    • Consider using disconnected ADO recordsets to move data between tiers.
    • You can pass Visual FoxPro data in strings, arrays, or ADO recordsets.
    • Passing Parameters:
      • Be careful when passing parameters.
      • Always use SafeArray when passing object references.
      • Passing by value:- Fastest and most efficient- Copies the parameters into a buffer- Sends all values at once
      • Passing by reference:- Sends a reference, but leaves the object back in the client.- Accessing the parameter scampers back to the client machine.
    • Always read the Late Breaking News! It contains important information such as Security configuration details.
    • Visit the Microsoft MTS Web site at www.microsoft.com/com/ for more information.
    • By default, MTS will create a maximum of 100 apartment threads for client work (per package). In Windows NT 4.0 Service Pack 4 (and later), you can tune the MTS activity thread pool. This will not affect the number of objects than can be created. It will simply configure the number that can be simultaneously in call. To tune the MTS activity thread pool:
      1. Open your Windows Registry using RegEdit and go to the package key:HKLM/Software/Microsoft/Transaction Server/Package/{your package GUID}
      2. Add a REG_DWORD named value:ThreadPoolMax
      3. Enter a value for ThreadPoolMax. Valid values are:0 to 0x7FFFFFFF


    In this article

    1. Overview
    2. Examining Framework Components
    3. Designating the Classes You Want
    4. Specifying Your Own Framework Components

    Show 7 more

    Lisa Slater Nicholls

    October 1998

    Summary: Describes how the Microsoft® Visual FoxPro® version 6.0 Application Framework, including the Application Wizard and Application Builder, can be used by the beginning developer to turn out polished applications and customized by the more experienced developer to create more detailed applications. (32 printed pages)

    Contents

    Overview Examining Framework Components Designating the Classes You Want Specifying Your Own Framework Components A Closer Look at the Standard Application Wizard A New Application Wizard A Few Parting Thoughts about Team Practices Appendix 1 Appendix 2 Appendix 3 Appendix 4Expand table

    Click to copy the appfrmwk sample application discussed in this article.

    Overview

    The Visual FoxPro 6.0 Application Framework offers a rapid development path for people with little experience in Visual FoxPro. With a few simple choices in the Application Wizard and the Application Builder, beginning developers can turn out polished and practical applications.

    Under the hood, however, the framework offers experienced developers and teams much more. This article shows you how to adapt the framework components so they fit your established Visual FoxPro requirements and practices.

    In the first section of this article you’ll learn about the files and components that support the framework and how they work together while you develop an application. This information is critical to moving beyond simply generating framework applications to experimenting with framework enhancements.

    The second section teaches you how to apply your experiences with the framework to multiple applications. After you’ve experimented with framework enhancements for a while, you will want to integrate your changes with the framework, for standard use by your development team. By customizing the files the Application Wizard uses to generate your application, you’ll make your revisions accessible to team members—without sacrificing the framework’s characteristic ease of use.

    Examining Framework Components

    This section shows where the framework gets its features and components, and how these application elements are automatically adjusted during your development process.

    Once you see how and where framework information is stored, you can begin to try different variations by editing the versions generated for a framework application. When you’re satisfied with your changes, you can use the techniques in the next section to migrate them to your team’s versions of the framework components.

    Note   Like most Visual FoxPro application development systems, the framework is composed of both object-oriented programming (OOP) class components and non-OOP files. This distinction is important because you adapt these two types of components in different ways; classes can be subclassed, while non-OOP files must be included as is or copied and pasted to get new versions for each application. The framework is minimally dependent on non-OOP files, as you’ll see here, but these files still exist.

    Throughout this article we’ll refer to the non-OOP framework files as templates, to distinguish these components from true classes.

    Framework Classes

    The Visual FoxPro 6.0 framework classes are of two types:

    1. Framework-specific classes. These classes have been written especially for the application framework and provide functionality specific to the framework. The standard versions of these classes are in the HOME( )+ Wizards folder, in the _FRAMEWK.VCX class library.
    2. Generic components. These features come from class libraries in the HOME( )+ FFC (Visual FoxPro Foundation Classes) folder.

    _FRAMEWK.VCX

    The _FRAMEWK.VCX class library (see Figure 1) contains all the classes written specifically to support the framework. Each framework application you create has an application-specific VCX containing subclasses of the _FRAMEWK.VCX components. The Application Wizard puts these subclasses in a class library named <Your projectname> plus a suffix to designate this library as one of the wizard-generated files. To distinguish these generated, empty subclasses, it adds a special prefix to the class names as well.

    Figure 1. _FRAMEWK.VCX framework-specific class library, as viewed in Class Browser, is found in the HOME( )+ Wizards folder.

    Framework superclass: _Application

    The _Application class is a required ancestor class, which means that this class or a subclass of this class is always required by the framework. This class provides application-wide manager services. For example, it manages a collection of modeless forms the user has opened.

    You designate a subclass of _Application simply by using CREATEOBJECT( ) or NEWOBJECT( ) to instantiate the subclass of your choice. (By default, the framework provides a main program to do this, but this PRG contains no required code.) When your designated _Application subclass has instantiated successfully, you call this object’s Show( ) method to start running the application.

    Note   In this article, we’ll refer to the object you instantiate from a subclass of _Application as the application object. We’ll continue to refer to “your subclass of _Application” to mean the class definition instantiating this object, which will be in a VCX belonging to your application (not _FRAMEWK.VCX). You’ll also see references to “_Application“, that refer specifically to code and properties you’ll find in the superclass located in _FRAMEWK.VCX.

    At run time, the application object instantiates other objects as necessary to fill all the roles represented by the other classes in _FRAMEWK.VCX except _Splash. The framework identifies these roles as important to various application functions, but, as you’ll see in this section, you have full control over how the roles are carried out.

    Note   The _Splash class is an anomaly in _FRAMEWK.VCX; it isn’t instantiated or used by the framework application directly. (If it were instantiated by the application object, your splash screen would appear too late to be useful.) Instead, _Splash merely provides a default splash screen with some of the same attributes as _Application (for example, your application name and copyright). The Application Builder transfers these attributes to your application’s subclass of _Splash at the same time it gives them to your application’s subclass of _Application, so they stay synchronized. The default main program delivered with a framework gives you one way to instantiate this splash screen before you instantiate your application object.

    You certainly don’t need to use the method shown in the default main program for your splash screen. In fact, many applications do not need a splash screen at all. For those that do, you may prefer to use the Visual FoxPro –b<file name> command-line switch, which displays a bitmap of your choice during startup, rather than a Visual FoxPro form of any description.

    Framework superclass: _FormMediator

    You’ll grasp most of the “roles” played by the subsidiary classes in _FRAMEWK.VCX easily, by reading their class names and descriptions. (If you can’t read the full class description when you examine _FRAMEWK.VCX classes in a project, try using the Class Browser.) However, you’ll notice a _FormMediator class whose purpose takes a little more explaining.

    You add an object descended from the _FormMediator custom class to any form or form class, to enable the form to communicate efficiently with the application object. This section will show you several reasons the form might want to use services of the application object. With a mediator, your form classes have access to these services, but the forms themselves remain free of complex framework-referencing code.

    The _FormMediator class is low-impact. It doesn’t use a lot of resources, and its presence will not prevent your forms from being used outside a framework application. Using this strategy, the framework can manage any forms or form classes your team prefers to use, without expecting them to have any special inheritance or features.

    Like _Application, _FormMediator class is a required ancestor class. You can create other mediator classes, as you can subclass _Application to suit your needs, but your mediators must descend from this ancestor.

    We’ll refer to _FormMediator and its descendents as the mediator object, because (strictly speaking) your forms will see it as the “application mediator” while the application object treats it as a “form mediator.”

    The Visual FoxPro 6.0 Form Wizards create forms designed to take advantage of mediators when the framework is available. You can see some simple examples of mediator use in the baseform class of HOME( )+ Wizards\WIZBASE.VCX.

    Examine _FormMediator‘s properties and methods, and you’ll see that you can do much more with the mediator in your own form classes. For example, the application object calls mediator methods and examines mediator properties during its DoTableOutput( ) method. (This method allows quick output based on tables in the current data session.) Your mediator for a specific form could:

    • SELECT a particular alias to be the focus of the output.
    • Prepare a query specifically for output purposes (and dispose of it after the output).
    • Inform the application object of specific classes and styles to be used by _GENHTML for this form.
    • Change the output dialog box caption to suit this form.

    The mediator also has methods and properties designed to specify context menus for the use of a particular form. If the application object receives this information from the mediator, it handles the management of this menu (sharing it between forms as necessary).

    You’ll find one example of mediator use in the ErrorLogViewer class. (This use is described in Appendix 1, which covers the options system.) A full discussion of the _FormMediator class is beyond the scope of this document. The more information you give a mediator or mediator subclass, however, the more fully your forms can use framework’s features, without making any significant changes to the forms themselves.

    Note   The _Application class includes a property, lEnableFormsAtRuntime (defaulting to .T.), which causes the application object to add mediators at run time to any form not having a mediator of its own. You can specify the mediator subclass that the application adds to a form at run time. Keep in mind, however, that mediators added at design time will have a more complete relationship with their form containers, because these forms can include code referencing their mediator members. During a form’s QueryUnload event, for example, the form can use the mediator to determine whether the form contains any unconfirmed changes. Without code in the form’s QueryUnload method, the mediator can’t intercede at this critical point.

    Additional _FRAMEWK.VCX classes

    The other classes in _FRAMEWK.VCX are all dialog box and toolbar classes to perform common functions within an application. None of these classes are required ancestors; you can substitute your own user interfaces and class hierarchies for these defaults at will. Two of them (_Dialog and _DocumentPicker) are abstract; that is, they are never instantiated directly, existing only to provide properties and methods to their descendent classes. Others will not instantiate unless you pick specific application characteristics. For example, if you don’t write “top form” applications (MDI applications in their own frames) you will never use _TopForm, the _FRAMEWK.VCX class that provides the MDI frame window object.

    Once you have examined these classes, and identified their roles, you will know which ones supply the types of services you need in applications you write—and, of these, you will identify the ones you wish to change.

    Designating the Classes You Want

    For each class role identified by the framework, the application object uses corresponding xxxClass and xxxClassLib properties to determine the classes you want. To change which class is instantiated for each role, you change the contents of these properties in your subclass of _Application.

    For example, _Application has cAboutBoxClass and cAboutBoxClassLib properties, and it uses these properties to decide what dialog box to show in its DoAboutBox( ) method (see Figure 2).

    Figure 2. Class and ClassLib property pairs in the _Application object

    If you fill out a class property but omit the matching Classlib property, _Application assumes that your designated class is in the same library as the _Application subclass you instantiated. If your _Application subclass is in the MyApplication.vcx and cAboutBoxClass has the value “MyAboutBox” but cAboutBoxClassLib is empty, a call to the Application object’s DoAboutBox( ) method instantiates a class called MyAboutBox in MyApplication.vcx.

    If you call the method instantiating one of the subsidiary classes when the matching class property is empty, _Application attempts to provide appropriate behavior to the specific situation. For example, if the cAboutBoxClass property is empty, DoAboutBox( ) will simply do nothing, because it has no alternative. By contrast, if the cErrorViewerClass property is empty, the _Application DisplayErrorLog( ) method will ask its cusError member object to use its default error log display instead.

    Except for the cMediatorClass and cMediatorClassLib properties, which must specify a class descending from _FormMediator in _FRAMEWK.VCX, remember that there are no restrictions on these dialog boxes and toolbars. You don’t have to subclass them from the classes in _FRAMEWK.VCX, or even follow their examples, in your own classes fulfilling these framework roles.

    Even when you design completely different classes, you will still benefit from investigating the defaults in _FRAMEWK.VCX, to see how they take advantage of their relationship with the framework. For example, all the classes descended from _Dialog have an ApplyAppAttributes( ) method. When the framework instantiates these classes, it checks for the existence of this method. If the ApplyAppAttributes( ) method exists, the application object passes a reference to itself to the form, using this method, before it calls the Show( ) method. In this way, the dialog box can derive any framework-specific information it needs before it becomes visible. For instance, the About Box dialog box might adjust its caption using the _Application.cCaption property.

    If the ApplyAppAttributes( ) method does not exist in yourcAboutBoxClass class, no harm is done. The _Application code still tries to harmonize your dialog box with its interface, in a limited way, by checking to see whether you’ve assigned any custom value to its Icon property. If you haven’t, _Application assigns the value in its cIcon property to your dialog box’s icon before calling its Show( ) method.

    Note   This strategy typifies the framework’s general behavior and goals:

    • It tries to make the best use of whatever material you include in the application.
    • When possible, it does not make restrictive assumptions about the nature of this material.
    • It avoids overriding any non-default behavior you may have specified.

    Investigating the default _Options dialog box class and _UserLogin default dialog boxes will also give you insight into the _Application options and user systems. While the dialog boxes themselves are not required, you will want to see how they interact with appropriate _Application properties and methods, so your own dialog boxes can take advantage of these framework features. In particular, the _Application options system has certain required elements, detailed in Appendix 1.

    FoxPro Foundation Generic Classes

    You may be surprised that _FRAMEWK.VCX contains only two required classes (the application and mediator objects), and in fact even when you add the other subsidiary classes, _FRAMEWK.VCX doesn’t contain much of the functionality you may expect in a Visual FoxPro application. You will not find code to perform table handling. You won’t find dialog boxes filling standard Visual FoxPro roles, such as a dialog box to select report destinations. You won’t find extensive error-handling code.

    _FRAMEWK.VCX doesn’t include this functionality because there is nothing framework-specific about these requirements. Instead, it makes use of several Visual FoxPro Foundation Classes libraries, useful to any framework or application, to perform these generic functions. The _Application superclass contains several members descending from FFC classes, and it instantiates objects from other FFC classes at run time as necessary. Then it wraps these objects, setting some of their properties and adding some specific code and behavior to make these instances of the FFC classes especially useful to the framework.

    For example, _Application relies on its cusError member, descended from the _Error object in FFC\_APP.VCX, to do most of its error handling, and to create an error log. However, as mentioned earlier, _Application code displays the error log using a framework-specific dialog box. The application object also sets the name and location of the error log table to match its own needs, rather than accepting _Error‘s default.

    The framework uses four FFC class libraries: _APP.VCX_TABLE.VCX_UI.VCX, and _REPORTS.VCX. Figure 3 shows these libraries in Class Browser views, as well as in a Classes tab for a framework application project.

    Figure 3. A framework application uses generic Visual FoxPro Foundation Classes, from HOME( )+ FFC folder, to supplement the framework-specific classes in _FRAMEWK.VCX.

    Unlike the subsidiary classes in _FRAMEWK.VCX, the FFC classes and their complex attributes are used directly by _Application, so you don’t specify alternative classes or class libraries for these objects. You can still specify your own copies of these class libraries, as you’ll see in the next section.

    If you examine the Project tab in Figure 3, or the project for any framework application, you’ll find this list of libraries built in. You’ll see _FRAMEWK.VCX, and there will be at least one class library containing the subclasses of _FRAMEWK.VCX for this application.

    You’ll see one more FFC library: _BASE.VCX, which contains the classes on which _FRAMEWK.VCX and all the FFC libraries are based. Your framework project must have access to a library called _BASE, containing all the classes found in _BASE. However, neither the framework nor the four FFC class libraries it uses require any specific behavior or attributes from these classes. You are free to create an entirely different _BASE.VCX with classes of the same name, perhaps descending from your team’s standard base library.

    Framework Templates

    The framework templates are of three types:

    1. Menu templates, a collection of Visual FoxPro menu definition files (.mnx and .mnt extensions)
    2. Metatable, an empty copy of the table the framework uses to store information about the documents (forms, reports, and labels) you use in your application
    3. Text, a collection of ASCII supporting files

    Unlike the .vcx files used by the framework, Visual FoxPro doesn’t deliver separate versions of these templates on disk. Because the templates are copied, rather than subclassed, for framework applications, the templates don’t need to be available to your project as separate files. Instead, these items are packed into a table, _FRAMEWK.DBF, found in the HOME( )+ Wizards folder. The Application Wizard unpacks the files when it generates your new application (see Figure 4).

    Figure 4. The Application Wizard copies template files from this _FRAMEWK.DBF table in HOME( )+ Wizards folder.

    Because the files don’t exist on disk, their template file names are largely irrelevant, except to the Application Wizard. Although we’ll use the template names here, keep in mind that their copies receive new names when the Wizard generates your application.

    Just as the framework identifies “dialog box roles” and supplies sample dialog boxes to fill those roles, it identifies some “menu roles,” and comes equipped with standard menus to meet these requirements. The roles are startup (the main menu for your application) and navigation (a context menu for those forms you identify as needing navigation on the menu).

    There are three template startup menus, each corresponding to one of the three application types described by the Application Builder as normaltop form, and moduleT_MAIN.MNX, is a standard “replace-style” Visual FoxPro menu. It’s used for normal-style applications, which take over the Visual FoxPro environment and replace _MSYSMENU with their own menu. T_TOP.MNX, for top form applications, looks identical to T_MAIN.MNX, but has some code changes important to a menu in an MDI frame. T_APPEND.MNX is an “append-style” menu, characteristic of modules, which are applications that add to the current environment rather than controlling it.

    There is one navigation menu template, T_GO.MNX. Its options correspond to the options available on the standard navigation toolbar (_NavToolbar in _FRAMEWK.VCX).

    Note   Because both T_GO.MNX and T_APPEND.MNX are “append-style” menus, they can exist as part of either _MSYSMENU or your top form menu. The Application Builder synchronizes your copy of T_GO.MNX to work with your normal- or topform-type application. However, if you change your application type manually rather than through the Application Builder, or if you want a module-type application that adds to an application in a top form, you may need to tell these menus which environment will hold them.

    You make this change in the General Options dialog box of the Menu Designer (select or clear the Top-Level Form check box). If you prefer, you can adjust the ObjType of the first record in the MNX programmatically, as the Application Builder does. See the UpdateMenu( ) method in HOME( )+ Wizards\APPBLDR.SCX for details.

    Like the document and toolbar classes in _FRAMEWK.VCX, the menu templates are not required. They simply provide good examples, and should give you a good start on learning how to use menus in a framework application.

    In particular, you’ll notice that the menus do not call procedural code directly, only application object methods. This practice ensures that the code is properly scoped, regardless of whether the MPR is built into an app, or whether the .app or .exe holding the MPR is still in scope when the menu option runs.

    Because Visual FoxPro menus are not object-oriented, they can’t easily hold a reference to the application object. To invoke application object methods, the menus use the object’s global public reference. This reference is #DEFINEd as APP_GLOBAL, in an application-specific header file, like this:

    #DEFINE APP_GLOBAL              goApp
    

    Here is an example menu command using the #DEFINEd constant (the Close option on the File menu):

    IIF(APP_GLOBAL.QueryDataSessionUnload( ),
      APP_GLOBAL.ReleaseForm( ),.T.)
    

    Each template menu header #INCLUDEs this header file. You can change the #DEFINE and recompile, and your menus will recognize the new application reference.

    Note   The application object can manage this public reference on its own (you don’t need to declare or release it). It knows which variable name to use by consulting its cReference property, which holds this name as a string. You can either assign the value in the program that instantiates your application object (as shown in the default main program) or you can assign this string to the cReference property of your _Application subclass at design time.

    The template menus are the only part of the framework using this global reference. If you wish, your forms and other objects can use the reference, too, but there are rarely good reasons to do this. Before you opt to use the global reference, think about ways you might pass and store a reference to the application object in your forms instead. If your forms have mediator objects, they have a built-in method to receive this reference any time they need it.

    Metatable Template

    _FRAMEWK.DBF contains records for T_META.DBF/FPT/CDX, the table holding information about documents for your application. Records in this table indicate whether a document should be treated as a “form” or “report”—and you can create other document types on your own.

    The document type designation is used by the framework dialog boxes descending from _DocumentPicker, to determine which documents are displayed to the user at run time. For example, the _ReportPicker dialog box will not display documents of “form” type, but the _FavoritePicker dialog box displays both forms and reports.

    However, document type as specified in the metatable does not dictate file type. A “report” type document might be a PRG, which called a query dialog box and then ran a report based on the results.

    The Application Builder creates and edits metatable records when you use the Builder to add forms and documents to the application. If you manually add a form or document to a framework project, the Project Hook object invokes the Builder to ask you for details about this document and fill out the metatable accordingly. Of course, you can also add records to the metatable manually.

    The Application Builder and the _FRAMEWK.VCX dialog boxes descending from _DocumentPicker rely on the default structure of this metatable. (You’ll find its structure detailed in**Appendix 2.) The dialog boxes derive from this table the information they need to invoke each type of document, including the options you’ve set in the Application Builder for each document. (Appendix 3 gives you a full list of _DocumentPicker subclasses and their assigned roles.)

    Just as you don’t have to use the _DocumentPicker dialog boxes, you don’t have to use the default metatable structure in a framework application. If you like the idea of the table, you could design a different structure and use it with dialog boxes with different logic to call the _Application methods that start forms and reports.

    Note   If you design a metatable with a different structure from the default, the application object can still take care of it for you. On startup, the metatable is validated for availability and appropriate structure. Once the metatable is validated, the application object holds the metatable name and location so this information is available to your application elements later, even though the application object makes no use of the metatable directly.

    Edit your _Application subclass’s ValidateMetatable( ) method to reflect your metatable structure if it differs from the default. No other changes to the standard _Application behavior should be necessary to accommodate your metatable strategy.

    You can also dispense entirely with a metatable in a framework application. No part of the framework, except the _DocumentPicker dialog boxes, expects the metatable to be present.

    For instance, you might have no need for the dialog boxes or data-driven document access in a simple application. In this case, you can eliminate the metatable and invoke all your reports and forms directly from menu options. Simply provide method calls such as APP_GLOBAL.DoForm( ) and APP_GLOBAL.DoReport( ) as menu bar options. Fill out the arguments in these methods directly in the command code for each menu option, according to the requirements of each form and report.

    Additional Text Templates

    _FRAMEWK.DBF holds copies of some additional text files copied for your application’s use.

    T_START.PRG is the template for the program that initializes your application object and shows the splash screen. Its behavior is well documented in comments you’ll find in the application-specific header file, described later. In addition, as just mentioned, it is not necessary. The program that creates your application object does not have to be the main program for your application, nor does it have to do any of the things that T_START.PRG does.

    For example, suppose your application is a “module type,” handling a particular type of chore for a larger application. Because it is a module, it does not issue a READ EVENTS line or disturb your larger application’s environment. It may or may not need to use the framework’s user log on capabilities; you may have set up a user logging system in the outer program. The outer application may be a framework application, or it may not. All these things will help you decide what kind of startup code you need for this application object.

    Let’s look at some sample code you might want to use for an accounting application. This .exe file is not a framework application, but it has a framework module added to it, which performs supervisor-level actions. Only some users are allowed to have access to this module. When your accounting application starts up, it may have an application manager object of its own, which performs its own login procedures. The method that decides whether to instantiate the framework module might look like this:

    IF THIS.UserIsSupervisor( )
       THIS.oSupervisorModule = ;
          NEWOBJECT(THIS.cMyFrameworkModuleSupervisorClass,;
                    THIS.cMySupervisorAppClassLib)
       IF VARTYPE(THIS.oSupervisorModule) = "O"
          * success
       ELSE
          * failure
       ENDIF
    ELSE
       IF VARTYPE(THIS.oSupervisorModule) = "O"
          * previous user was a supervisor
          THIS.oSupervisorModule.Release()
       ENDIF
    ENDIF
    

    This code does not handle the public reference variable, a splash screen, or any of the other items in T_START.PRG.

    You may not need the public reference variable at all because, in this example, your framework application is securely scoped to your larger application manager object. However, if your module application has menus that use the global reference to invoke your application object, you might assign the correct variable name to THIS.oSupervisorModule.cReference just above the first ELSE statement in the preceding sample code (where you see the “* success” comment). This is the strategy you see in T_START.PRG.

    Note   If many different outer applications will use this module, you will prefer to assign the appropriate cReference string in the class, rather than in this method (so you only need to do it once). You can assign this value to cReference either in the Properties window or in code during startup procedures for the application object. Either way, an assign method on the cReference property in _Application does the rest.

    T_META.H is the template name for the application-specific header file, just mentioned in the section on menu templates. Only the menus and T_START.PRG use this header file, so it is up to you whether you use it, and how you use it. In the preceding example, you might not use it at all, or you might use only its APP_GLOBAL define to set the application object’s global reference.

    The framework uses a few more text templates:

    • T_CONFIG.FPWNot surprisingly, provides a template for the config.fpw generated for your application. The template version gives new Visual FoxPro developers some ideas about what the config.fpw is for (it’s mostly comments); you will almost certainly wish to edit this file to meet your own standards.
    • T_LOG.TXTProvides a startup file for the “action log” the Project Hook will write during the life of your application to let you know what changes it has made to your application while you worked with the project.
    • T_HEAD.TXTProvides a standard header that the Application Wizard uses when generating your application-specific copies of framework templates. You might want to revise T_HEAD.TXT to include your own copyright notices, especially after you’ve edited the rest of the templates.

    Specifying Your Own Framework Components

    If you’ve done any development at all, you’ve undoubtedly experienced moments in which you identify something you wish to abstract from the process of developing a single application. You’ve done it too many times, you know how to do it, and now it’s time you figure out the best way to do it—so you never have to do it again.

    In OOP terms, this is the time to develop a superclass to handle this function, so you can reuse its features. In template terms, this is the time to edit the template you copy for each application’s use. In the Visual FoxPro 6.0 application framework’s mixed environment, as you know, we have both types of components.

    We’ll quickly review how these components are managed automatically by the Application Wizard and Builder during your development cycle. Then we’ll turn our attention to how you integrate your own superclasses and edited templates into this system.

    Framework Components During Your Application Lifecycle

    When you choose to create a new framework application, the Application Wizard takes your choices for a location and project name and generates a project file. If you select the Create project directory structure check box, the Application Wizard also creates a directory tree under the project directory. It adds _FRAMEWK.VCX and the required foundation class libraries to this project. It also adds a class library with appropriate application-specific subclasses of _FRAMEWK.VCX.

    The Application Wizard then adds template-generated, application-specific versions of all the non-OOP components the application needs. As you probably realize, the Application Wizard copies these files out of the memo fields in _FRAMEWK.DBF.

    _FRAMEWK.DBF contains two more records we haven’t mentioned yet: T_META.VCX and T_META.VCT. These records hold straight subclasses of the classes in _FRAMEWK.VCX, and they are copied out to disk to provide your application-specific class library.

    Note   T_META.VCX is not a template. It is just a convenient way for the Application Wizard to hold these subclasses, and is not part of your classes’ inheritance tree. Your subclasses descend directly from _FRAMEWK.VCX when the Application Wizard creates them, and thereafter will inherit directly from _FRAMEWK.VCX.

    Once your new framework project exists, the Application Wizard builds it for the first time. It also associates this project with a special Project Hook object, designed to invoke the Application Builder. The Application Wizard shows you the new project and invokes the Application Builder.

    At this point, the Application Builder takes over. The Application Builder provides an interface you can use to customize the framework aspects of any framework-enabled project, throughout the life of the project.

    You can use the Application Builder to customize various cosmetic features of the application object, such as its icon. When you make these choices, the Application Builder stores them in the appropriate properties of your _Application subclass. (In some cases, it also stores them in the matching _Splash subclass properties.)

    In addition, the Application Builder gives you a chance to identify data sources, forms, and reports you’d like to associate with this project. It gives you convenient access to the data, form, and report wizards as you work, in case you want to generate new data structures and documents. For inexperienced developers, the Application Builder provides a visual way to associate data structures directly with forms and reports, by providing options to invoke report and form wizards each time you add a new data source.

    Whether you choose to generate reports and forms using the wizards or to create your own, the Application Builder and its associated Project Hook object help you make decisions about framework-specific use of these documents. (Should a report show up in the Report Picker dialog box, or is it only for internal use? Should a form have a navigation toolbar?) It stores these decisions in your framework metatable.

    As you think about these automated elements of a framework development cycle, you’ll see a clear difference between the changes you can effect if you change the Application Wizard, or generation process, and the changes you can effect by editing the Application Builder and Project Hook. The files provided by the Wizard, in advance of development, represent your standard method of development. The changes made thereafter, through the Builder and Project Hook, represent customization you can do for this single application.

    The balance of this article concentrates on enhancing the Wizard to provide the appropriate framework components when you begin a new application. Once you have established how you want to enhance the startup components, you will think of many ways you can change the Builder and the Project Hook, to take advantage of your components’ special features, during the rest of the development cycle.

    Note   An important change in versions after Visual FoxPro 6.0 makes it easy for you to customize the Application Builder to match your style of framework use. Rather than directly invoking the default appbldr.scx, the default Application Builder in later versions is a PRG.

    The PRG makes some critical evaluations before it displays a Builder interface. For example, it checks to see whether the project has an associated Project Hook object, and whether this Project Hook object specifies a builder in its cBuilder property. See HOME( )+ Wizards\APPBLDR.PRG for details. You will find it easy to adopt this strategy, or to edit appbldr.prg to meet your own needs for displaying the Builder interface of your choice.

    A preview version of appbldr.prg is included with the source for this article. See appbldr.txt for instructions on making this new Application Builder available automatically from the VFP interface, similar to the new wizard components delivered as part of the document.

    A Closer Look at the Standard Application Wizard

    You’ll find the Visual FoxPro 6.0 Application Wizard files in your HOME( )+ Wizards folder. When you invoke the Application Wizard from the Tools menu, it calls appwiz.prg, which in turn invokes the dialog box in Figure 5, provided by appwiz.scx.

    Figure 5. The standard Visual FoxPro 6.0 Application Wizard dialog box provided by appwiz.scx

    When you choose a project name and location, appwiz.prg invokes HOME( )+ Wizards\WZAPP.APP, the Visual FoxPro 5.0 Application Wizard, with some special parameters.

    The older wizard contained in wzapp.app does most of the work of creating your new project files. The Visual FoxPro 5.0 Application Wizard determines that you are in a special automated mode from the object reference it receives as one parameter and does not show its original interface. It evaluates a set of preferences received from this object reference, and proceeds with the generation process.

    The standard implementation has a number of constraints:

    • Your application subclasses descend directly from _FRAMEWK.VCX. This prevents your adding superclass levels with your own enhancements to the framework, and you certainly can’t specify different superclasses when you generate different “styles” of applications.
    • Your copies of the ancestor classes, in _FRAMEWK.VCX and FFC libraries, are presumed to be in the HOME( )+ Wizards and HOME( )+ FFC directories. Because these ancestor classes are built into your framework applications, and therefore require recompilation during a build, you have to give all team members write privileges to these locations or they can’t use the Application Wizard to start new framework applications. In addition, the fixed locations hamper version control; you may wish to retain versions of ancestor classes specific to older framework applications, even when Microsoft delivers new FFC and Wizards folders.
    • Your non-OOP components are always generated out of HOME( )+ Wizards\_FRAMEWK.DBF. The templates are not easily accessible for editing. The assumed location of _FRAMEWK.DBF prevents you from using different customized template versions for different types of apps, and also presents the same location problems (write privileges and versioning) that affect your use of the framework class libraries. As with your application subclasses, you can’t designate different templates when you generate different types of applications.
    • You have no opportunity to assign a custom Project Hook to the project.

    To allow you to design and deploy customized framework components, a revised Application Wizard should, at minimum, address these points.

    You can make the required changes without major adjustment of the current Application Wizard code, but some additional architectural work provides more room for other enhancements later.

    A New Application Wizard

    If you DO NEWAPPWIZ.PRG, provided in the source code for this article, you will get a dialog box almost identical to Figure 5, and functionally equivalent to the original dialog box. The only difference you’ll notice is a request, on startup, asking you if you wish to register this wizard in your HOME( )+ Wizards\WIZARD.DBF table for future use (see Figure 6).

    Figure 6. The Newappwiz.prg wizard classes can be registered to HOME( )+ Wizards\WIZARD.DBF so you can choose them from the Tools Wizards menu later.

    Though your newly instantiated wizard class calls the old Visual FoxPro 5.0 Wizard code just as the original one did, its internal construction allows completely new generation code to replace this approach in a future version.

    You can call newappwiz.prg with a great deal of information packed into its second parameter, to indicate what wizard class should instantiate and what this wizard class should do once instantiated.

    Why the second parameter, rather than the first? Newappwiz.prg, like appwiz.prg, is designed with the standard wizard.app in mind. wizard.app, the application invoked by the Tools Wizards menu option for all wizard types, uses its registration table, HOME( )+ Wizards\WIZARD.DBF to find the appropriate wizard program to run. Wizard.app passes other information in its first parameter to the wizard program (in this case, newappwiz.prg). Wizard.app passes the contents of the Parms field of wizard.dbf, as the second parameter.

    If you choose Yes in the dialog box in Figure 6, the NewAppWizBaseBehavior class becomes a new choice in the registration table, and fills out its options in the Parms field. Additional NewAppWizBaseBehavior subclasses will do the same thing, registering their own subclasses as separate entries. Once a class is registered in wizard.dbf, you don’t have to call newappwiz.prg directly again.

    If you’ve chosen Yes in the dialog box in Figure 6 and also choose to register the wizard subclass we investigate in the next section, when you next choose the Application Wizard from the Tools menu, you’ll get a choice, as you can see in Figure 7.

    Figure 7. Select your Application Wizard du jour from the Tools Wizards option—once you have more than a single Application Wizard listed in your HOME( )+ Wizards\WIZARD.DBF table.

    An Extended Subclass of the New Wizard: AppWizReinherit

    With an enhanced architecture in place, we can address the issues of component-generation we’ve raised.

    Run newappwiz.prg again, this time with a second parameter indicating a different wizard subclass to instantiate:

      
    

    You should get another message box, similar to Figure 6, asking you if you want to register this subclass in the wizard.dbf table. When you’ve dismissed the message box, you see the dialog box in Figure 8.

    Figure 8. Re-inheritance Application Wizard, page 1

    The first page of this dialog box contains exactly the same options as the standard Application Wizard.

    Note   You’ll find all the visual classes used in the new wizards in newappwiz.vcx, as part of the source code for this article. The container you see on this page of the AppWizFormReinherit class is the same container class used in AppWizFormStandard. You can read more about these dialog box classes in Appendix 4.

    Each subsequent page of the dialog box addresses one of our concerns with the way the original Application Wizard delivers components, and includes some information about how it works. (Figure 9 shows you pages 2 and 3.) Each option defaults to the same behavior you’d get from the original Application Wizard—you don’t need to fill out information on all pages.

    Figure 9. Pages 2 and 3 of the Re-inherit App Wizard provide a layer of superclasses and the locations of your FFC and _FRAMEWK.VCX libraries for this framework application.

    If you change the parent VCX as suggested on the second page of the dialog box, you can have one or more layers of superclasses between your application’s subclasses of _FRAMEWK.VCX. You’ll create team-specific enhancements in these layers.

    Note   This version of the Application Wizard will create the initial classes for you, as subclasses of the components in _FRAMEWK.VCX, if you specify a VCX name that does not exist. Later, you can create more layers of subclasses from the one the Application Wizard derived from _FRAMEWK.VCX, and designate your subclass layer in this dialog box as appropriate. The VCX you designate on the second page of this dialog box should always conform to the following rules:

    • Be the immediate superclasses (parent classes) of the application-specific VCX for this application.–and–
    • Include all the required subclasses of _FRAMEWK.VCX, with the same names as the _FRAMEWK ancestor classes.

    You may want several different branches of your team-specific class levels, to match different types of framework applications you commonly create. For example, you could have one superclass set with your team’s options for a framework module and another one with your team’s topform custom attributes (including the class and classlibrary for your subclass of _topform to provide the correct frame).

    Note   These branches, or types, are not restricted to the “styles” or options you see represented in the Application Builder. They are just part of the normal process of subclassing and enhancing a class tree.

    For example, you may decide to create Active Documents as framework applications. To do so, you’ll need an _Application subclass that is aware of its hosted environment, and makes certain interface decisions accordingly. You’ll also need an ActiveDoc subclass that is aware of the framework’s capabilities and calls application object methods in response to browser-triggered events, just as the menu templates invoke framework behavior.

    Now that you can insert class levels between _FRAMEWK.VCX and your application-specific level, you can make the implementation of these features standard across applications.

    If you change the locations of the FFC and _FRAMEWK.VCX libraries on the “Ancestors” page, the Application Wizard will place appropriate copies of the required class libraries in your specified locations if they don’t exist. The Application Wizard also ensures that your copy of _FRAMEWK.VCX inherits from the proper version of FFC, and that your parent classes point to the proper version of _FRAMEWK.VCX.

    Note   As mentioned in the section “FoxPro Foundation Generic Classes,” your FFC location can include your own version of _BASE.VCX. Your _BASE.VCX does not have to have the same code or custom properties as the original _BASE.VCX, but like your parent classes, your _BASE must include classes descended from the same Visual FoxPro internal classes, with the same names, as the classes in the original _BASE.

    Other FFC libraries, not used in the framework and not described in this article, will not necessarily work with your own _BASE.VCX. For example, if your application uses _GENHTML, the _HTML.VCX library relies on code in the HOME( ) + FFC\_BASE.VCX library. If you use other FFC libraries in your framework application, you may have two _BASE.VCXs included in your project—this is perfectly normal.

    The Application Wizard then focuses on your template files on the next page of the dialog box. If you set a location for your template files, the Application Wizard will create fresh copies of these files (by copying them from the original _FRAMEWK.DBF), ready for you to edit.

    In each case, if the files are already in the locations you supply, the Application Wizard will use the ones you have.

    The last page of the dialog box allows you to pick a Project Hook. The original AppHook class in HOME( ) + Wizards\APPHOOK.VCX is the required ancestor class for a Project Hook designed to work with this application framework, but you can add a lot of team-specific features to your Project Hook subclass. The Application Wizard attempts to verify that the class you specify on this page descends from the appropriate AppHook class.

    When you generate your application, the Application Wizard will create a new set of straight subclasses from your parent VCX (or _FRAMEWK.VCX, if you haven’t changed the default on the “Parents” page). These subclasses become the new T_META.VCX/VCT records in _FRAMEWK.DBF. The Wizard appends new contents for all the other template records of _FRAMEWK.DBF from the template folder, if you’ve named one.

    Note   The first time you and the Application Wizard perform these tasks, it won’t make much difference to the final results. Once the Wizard gives you editable superclass layers and your own copies of the templates, however, you have all the architecture necessary to customize the framework for subsequent uses of the Application Wizard.

    Having replaced _FRAMEWK.DBF records, the Application Wizard proceeds to create your new application much as before, inserting information about your designated Project Hook class at the appropriate time.

    All the “enhanced” Wizard actions are tuned to respect the current setting of the lDelegateToOriginalAppWizard switch, which indicates whether the Visual FoxPro 5.0 Application Wizard code is running or if new code is creating the project. For example, because the original code only looks in the HOME( )+ Wizards folder for _FRAMEWK.DBF, if you have indicated a different place for your _FRAMEWK.DBF (on the “Templates” page) this table will be copied to HOME( )+Wizards before wzapp.app runs. (The first time this occurs, the new Wizard copies your original _FRAMEWK.DBF to a backup file in the HOME( ) + Wizards folder.) Presumably, newer code simply uses your templates table wherever you’ve placed it.

    When you use this Wizard to generate a framework application it saves information about your preferred parent classes, as well as the locations of your FFC and _FRAMEWK libraries and template files, to special _FRAMEWK.DBF records. You won’t need to enter this information, unless you wish to change it. This release of the Application Wizard doesn’t save information about the custom Project Hook subclass you may have specified. However, the next section will show you how to put this information into the Parms of wizard.dbf for default use.

    Note   Because the Application Wizard reads its stored information out of _FRAMEWK.DBF, it can’t get the location of _FRAMEWK.DBF from a stored record! However, you can put this information into the Parms field of wizard.dbf, as described in the next section, so all your developers use the proper version of _FRAMEWK.DBF without having to look for it.

    You may even decide to use a version of this Wizard class, or of its associated dialog box, that only allows some developers to change the “advanced” pages. Other team members can fill out standard information on Page 1, but they’ll still get your improved versions of all the framework components.

    Registering Additional Wizard Subclasses and Customized Records

    The new Application Wizard provides the opportunity to register each subclass of its superclass separately in the wizard.dbf table. The wizard stores its class name and location in the Parms field of its own wizard.dbf record.

    However, you can add more information in the Parms field. You can even store multiple entries in the wizard.dbf for a single subclass, with differently tuned Parms values. The Application Wizard, once instantiated, uses this additional information.

    Here’s the full list of nine options you can pass in the second parameter, or place in the Parms field, for use by NewAppWizBaseBehavior and its subclasses. All #DEFINEs mentioned in this list are in the newappwiz.h header file associated with newappwiz.prg:

    These three options instantiate the Wizard:

    • Wizard classMust descend from #DEFINEd APPWIZSUPERCLASS, defaults to NEWAPPWIZSUPERCLASS.
    • Wizard classlibLibrary containing wizard class, defaults to NEWAPPWIZ.PRG.
    • .App or .exe file nameOptional file, containing the wizard class library.

    These six options are used by the Application Wizard after it instantiates:

    • Wizard form classMust descend from #DEFINEd APPWIZFORMSUPERCLASS, defaults to #DEFINEd NEWAPPWIZFORMSTANDARD.
    • Wizard form classlibLibrary containing the form class, defaults to NEWAPPWIZ.VCX.
    • .App or .exe file nameOptional file containing the wizard form class library.
    • Project Hook classThe Project Hook class you want to associate with this project, if you don’t want to use the default Project Hook class associated with framework-enabled projects. This class should descend from the AppHook class in HOME( )+ “Wizards\APPHOOK.VXC”, so it includes the default functionality, but can include enhancements required by your team.
    • Project Hook classlibThe class library containing the Project Hook class you choose to associate with this project.
    • Template DBFHolding application components, defaults to HOME( )+ Wizards\_FRAMEWK.DBF (#DEFINED as APPWIZTEMPLATETABLE).

    Store these values delimited by commas or carriage returns in the Parms field of wizard.dbf. Similarly, if you call newappwiz.prg directly, you can pass all this information as the program’s second parameter, as a single string delimited with commas or carriage returns.

    After you’ve registered the AppWizReinherit class, the Parms field for this class’ record in wizard.dbf contains the following information:

    APPWIZREINHERIT,<fullpath>\newappwiz.fxp,,AppWizFormReinherit, <fullpath>\NEWAPPWIZ.VCX,,APPHOOK, <fullpath of HOME()+ "Wizards"> \APPHOOK.VCX, <fullpath of HOME()+ "Wizards"> _framewk.DBF
    

    You could run the NEWAPPWIZ program, passing the same string as its second parameter, to get AppWizReinherit‘s default behavior.

    Using our ActiveDoc example just shown, you could create a wizard.dbf entry that invokes the same Wizard class but defaults to a different parent VCX and different menu templates than the rest of your framework applications.

    To accomplish this, you’d edit the information in the ninth value for this row of the wizard.dbf table, which indicates Template DBF, by editing the Parms field.

    Your new row in the table contains the same string in the Parms field, except for the section following the last comma, which points to a new template table. Your special ActiveDoc copy of _FRAMEWK.DBF holds your special Active Document menu templates and superclass information.

    Next, suppose you decide that your ActiveDocument framework applications need a special Project Hook subclass, not just special superclasses and menu templates. You could specify this hook automatically, in the seventh and eighth sections of the Parms field. You might even subclass the AppWizFormReinherit dialog box, to disable the last page of this dialog box for ActiveDocument-type applications, by changing the fourth and fifth sections of the Parms field. (This way, your team members would always use the right Project Hook class when generating this type of framework application.)

    If you made all these changes, this new entry in the wizard.dbf table might have a Parms field that looked like this:

    APPWIZREINHERIT,<fullpath>\newappwiz.fxp,,MyAppWizActiveDocumentDialog, <fullpath>\MyAppWizDialogs.VCX,,MyActiveDocumentAppHookClass, <fullpath> \MyHooks.VCX, <fullpath>\MyTemplates.DBF

    You would also edit the Name field in wizard.dbf for this entry, perhaps to something like “Active Document Framework Application,” to distinguish this entry from your standard values for the AppWizReinherit class.

    When one of your team members accessed the Tools Wizards option from the system menu, “Active Document Framework Application” would now appear on the list of available Wizards, as part of the list you saw in Figure 7. The developer could automatically create the right type of framework application, without making any special choices.

    A Few Parting Thoughts about Team Practices

    You’ll notice a check box in the Reinheritance Wizard‘s dialog box, indicating that you can omit message boxes and generate your new application with no warning dialog boxes or user interaction. Although this is a helpful option once you’ve used this Wizard a few times, please be sure to read all the message boxes, and the information in the edit boxes on the various pages of this dialog box, at least once.

    Any developer’s tool, especially one that edits visual class libraries and other metafiles as extensively as this one does, can potentially cause problems if the system is low on resources. The Help text available within this Wizard attempts to point out its potential trouble spots, so you can close other applications as needed, and have a good idea of what to expect at each step. Other caveats, such as incompletely validated options in this preliminary version, are indicated in the Help text as well.

    You also see a More Info button, which provides an overview of the issues this class is meant to address, and how you can expect it to behave (see Figure 10).

    Figure 10. Wizard documentation under the More Info button

    Beyond its stated purpose to enhance the Application Wizard, AppWizReinherit and its dialog box class try to give you a good model for tool documentation, both at design and run time. The dialog box’s NewAppWiz_Documentation( )GetUserInfo( ), and DisplayDocumentation( ) methods should give you several ideas for implementation of run-time documentation. Newappwiz.prg has a demonstration procedure, BuilderGetDocumentation( ), which shows you how you can apply these ideas to design time documentation for Builders as well. A final demonstration procedure in newappwiz.prg, ReadDocs( ), shows you another aspect of this process.

    Each documentation idea demonstrated here is a variation on a theme: Text is held (using various methods) within the VCX, so it travels with the VCX and will not get lost no matter how widely you distribute the library.

    Whether you use these particular implementations is not important; in many cases you’ll be just as well off if you create a text file with documentation and use Visual FoxPro’s FileToString( ) method to read this information for display by the tool whenever necessary.

    No matter how you decide to implement it, documentation that helps your team better understand the intended use, extension possibilities, and limitations of the tools you build is critical to their adoption and successful use.

    A framework is, in itself, a kind of abstraction, a level above daily activities. Enhancements to a framework represent yet another level of abstraction. Your team will benefit from all the extra attention you can give to communicating your goals for this process.

    With any framework, you can efficiently prototype applications and build complete lightweight applications. With a framework set up the way your team operates, you can accomplish these goals without sacrificing quality, depth, or your normal habits of development. With a framework set to deliver your standard components and practices automatically, even new developers can make meaningful, rewarding contributions to your team effort.

    Appendix 1: The User Option System

    The framework employs a user-registration system based on a user table that is created by the application object if not found at run time. The application object uses the cUserTableName property to set the name and location of this table. If no path is supplied in this property, the location will be set by the cAppFolder property.

    Note    By default, the application object sets cAppFolder to the location of the APP or EXE that instantiated it. If, for some reason, the application object was instantiated outside a compiled APP or EXE container, cAppFolder contains the location of the application object’s VCX.

    If necessary, the application object creates this table in the appropriate location, using the following code (excerpted from the CreateUserTable( ) method):

    lcIDField = THIS.cUserTableIDField
    lcLevelField = THIS.cUserTableLevelField
    * names of two generic-requirement fields,
    * User ID and level, are specified by
    * _Application properties in case you
    * wish to match them to some existing system
    CREATE TABLE   (tcTable) ;
       ((lcIDField) C(60), ;
       (lcLevelField) I, ;
       UserPass  M NOCPTRANS, ;
       UserOpts  M NOCPTRANS, ;
       UserFave  M NOCPTRANS, ;
       UserMacro M NOCPTRANS, ;
       UserNotes M )
    INDEX ON PADR(ALLTR(&lcIDField.),60) TAG ID
    * create a case-sensitive, exact word match
    INDEX ON PADR(UPPER(ALLTR(&lcIDField.)),60) TAG ID_Upper
    * create a case-insensitive, exact word match
    INDEX ON DELETED( ) TAG IfDeleted
    

    If you don’t opt to have users log in and identify themselves in this application, this table is still created. In this case it supplies a default record, representing “all users,” so user macros, favorites, and options can still be stored in this table on an application-wide basis.

    Note   Because of their “global” nature in Visual FoxPro, user macro saving and setting features are only available to framework applications that issue READ EVENTS. Module applications are not allowed to edit the macro set.

    When a user logs in, his password is evaluated using the user table’s UserPass field. A SetUserPermissions( ) method, abstract in the base, is called at this time so the user’s level can be checked in order to make appropriate changes to the application and menu options as well.

    If the login is successful (or when the application starts up assuming no user login for this application), user name and level are stored in the cCurrentUser and iCurrentUserLevel properties.

    User macros, favorites, and options are set from the user’s record in the user table. The _Application code handling macros rely on standard Visual FoxPro abilities to SAVE and RESTORE macros to and from the UserMacro memo field. The favorites system uses an easy-to-read ASCII format in the UserFave memofield. However the options system and the UserOptions field deserve more explanation.

    The user table stores option information in its UserOptions memo field, by SAVEing the contents of a local array. This local array is RESTOREd and copied into a member array, aCurrentUserOpts, to establish user options when the current user is set.

    The array format is fixed, and yet extremely flexible in the types of user options that can be stored. The allowable options include SETs and member properties, and the options should be specified as being “global” to the application or private to a datasession. The array is laid out, to specify these attributes of each option, in four columns, as follows.Expand table

    User Option Array Column 1Column 2Column 3Column 4
    Item nameFor a SET command, the item you’re setting, same as what you’d pass to the SET( ) function.
    For an object, the property you wish to set. Can be the Member.Property you wish to set.
    Value for this itemProperty (.F.)
    or SET (.T.) ?
    Session (.F.)
    or Global (.T.) ?

    Each time a user logs in, the application method ApplyGlobalUserOptions( ) applies SET options and application object property values for all array rows with .T. in the fourth column. The mediator object has the responsibility to call the application method ApplyUserOptionsForSession( ), on your instructions, passing a reference to its parent form. This method applies SET options and form property values for all array rows with .F. in the fourth column.

    The _Options dialog box supplied in _FRAMEWK.VCX gives you examples of all the combinations that can be created for a user option using this array, although its contents are merely examples. It shows you how the user options stored in an array can be expressed as a user interface, giving the user a chance to make changes. It also shows how results of a user-option-setting can be “translated” back into the user options array for use during this login, or saved as defaults to the user preference table.

    You will note that, when the user options to apply changes to the current settings, the Options dialog box reinvokes ApplyGlobalUserOptions( ) and then iterates through the available forms, giving their mediators a chance to reapply session settings if they’re set to do so.

    In many cases, a “global” setting can transferred to forms as well. For example, the _ErrorLogViewer dialog box has a mediator that checks the application’s cTextDisplayFont setting. This is a global user option, because it provides a chance for the user to specify a text font across all the UI of an application. The mediator transfers the value of the cTextDisplayFont to a property of the same name belonging to its parent dialog box. An assign method on this property then applies the fontname value to all members of the dialog box that should reflect the setting.

    Appendix 2: The Default Metatable Structure

    This table shows you the default structure of the framework’s metatable. Appendix 3 shows you how the default _FRAMEWK.VCX dialog boxes use this information.Expand table

    FieldNameTypeUse
    Doc_typeCThis field contains a character to distinguish between document types. Currently, “F” is used for “forms” and “R” is used for “reports.” But this designation just determines how the document type is presented in the interface, not necessarily what type of Visual FoxPro source code file underlies the document. See Alt_Exec and Doc_wrap fields, below.More document types may be added. The framework already contains one extra type, “A,” specifically reserved for you to add application information. The framework will not use “A”-type metatable records in any way, so the reservation of this type simply allows you to use metatable records, or perhaps one metatable header record, as a convenient place for system storage. In most cases, you would want to transfer the contents of such a record to application properties on startup.
    Doc_descrCThe “caption” or long description you want to show up in document picker lists.
    Doc_execMThe name of the file to be run, usually an .scx or .frx file. In the case of a class to be instantiated, this is the .vcx file name.For Form-type documents, the file extension is assumed to be .scx unless this entry is marked “Doc_wrap” (see below) or the Doc_class field is filled out, in which case the extension is assumed to be .vcx.For Report-type documents, the file extension will default to .frx unless this entry is marked “Doc_wrap”. If no .frx file exists by that name, the application object looks for an .lbx file.In all cases, you may also fill out the file extension explicitly.In all cases, if you Include the file to be run in the project, you need not use paths in this field. If you wish to Exclude the file from the project, you may use path information. Assuming your applications install their subsidiary Excluded files to the appropriately located folder, relative pathing should work in the metatable, and is probably the best policy in this case!
    Doc_classMThe class to be instanced, where the Doc_exec is a .vcx file
    Doc_newLMark this .T. for a Form-type document you wish to show up in the FileNew list. When the application object instantiates a form from the FileNew list, it sets its own lAddingNewDocument property to .T. This practice gives the form a chance to choose between loading an existing document or a blank document during the form’s initialization procedures.In many cases, the form delegates this process to its mediator object. The mediator object saves this information for later use.If you do not use a mediator, you may wish to save this information to a form property; you can’t expect the application object’s lAddingNewDocument to reflect the status of any particular form except during the initialization process of that form.For a Report-type document, this field denotes an editable report (new report contents, or even a new report from a template). This capability isn’t currently implemented.
    Doc_openLMark this .T. for a Form-type document you wish to show up in the FileOpen list.For a Report-type document, this field denotes a runnable report or label and will place the item in the report picker list.
    Doc_singleLMark this .T. for a Form-type document that is modeless but should only have one instance. The application object will bring it forward, rather than create a second instance, if the user chooses it a second time.
    Doc_noshowLMark this .T. for a Form-type document that you wish to .Show( ) yourself after additional manipulation, rather than allowing the DoForm( ) method to perform the .Show( ).Note   You will have to manipulate the application’s forms collection or the current _SCREEN.Forms( ) contents to get a reference to this form, so you can manipulate the form and then .Show it when you are ready. If you need this reference immediately, the best place to get it is probably the application object’s aForms[] member array. At this moment, the application object’s last-instantiated form is the one for which you want the reference, and the application object’s nFormCount property has just been refreshed. Therefore, .aForms[THIS.nFormCount] gives you the reference you need when you’re in an application object method (in other code, replace THIS with a reference to the application object). You can see an example of this usage in the _Application‘s DoFormNoShow( ) method.You can create Doc_Wrap programs as described in the entry for the next field. Your wrapper program can take advantage of the DoFormNoShow( ) method, receive its return value (a reference to the form or formset object), and proceed to do whatever you want with it.
    Doc_wrapLIf this field is marked .T. indicating a “wrapped” document, the application’s DoProgram( ) method will run instead of its DoReport( )/DoLabel( ) or DoForm( ) method.If you omit the file extension, the DoProgram( ) method uses the standard Visual FoxPro extension hierarchy to figure out what file you wish to run (“.exe .app .fxp .prg”).
    Doc_goLIf this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard Go context menu for navigation. The menu name is configurable using the application object’s cGoMenuFile property. This field is not used for report-type documents.
    Doc_navLIf this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard navigation toolbar for navigation. The class is configurable using the application object’s cNavToolbarClass and cNavToolbarClassLib properties. This field is not used for report-type documents.
    Alt_execMIf this field is filled out, it takes precedence over the Doc_exec field just described. When the user makes a document choice, the _DocumentPicker’s ExecDocument( ) method converts the contents of this field into a string and runs that string as a macro.Your Alt_exec statement can be anything you choose, and it can use attributes of the metatable, including the Properties field (below) however you want. For example, you can choose to have the metatable editable (on disk) rather than included in the APP/EXE, and you can place information in the Properties field dynamically at run time. Your document would then be able to be “aware” of this information by examining the current contents of the Properties field.
    PropertiesMThis memo field is not used by the framework in any way. It’s for developer use, primarily in conjunction with the Alt_exec field.
    User_notesMThis memo field is not used by the framework in any way. It can be used for notes that would be displayed as Help text for a particular form or report, and so on.

    Appendix 3: Default Document- Management Elements of the Framework

    The framework accesses metatable information through the _DocumentPicker classes. _DocumentPicker is an abstract standard dialog box class, which contains a picklist and a couple of buttons. The working _DocumentPicker subclasses each have their own way of using the information in the metatable to perform two tasks:

    • Show the documents in the picklist.
    • Run the appropriate action when the user picks a document.

    Each subclass stores the relevant metatable fields into an array, which serves as the data source for the list box in the dialog box. The same array holds the metatable information that will eventually act on the user’s choice.

    The _DocumentPicker superclass has an abstract FillDocumentArray( ) method, designed to perform the first service during the dialog box Init( ), and another abstract method called ExecDocument( ), which is triggered whenever/however the user makes a selection from the document list.

    The _DocumentPicker class receives a parameter from the application object. Each subclass of _DocumentPicker uses the parameter to determine which of two states it is supposed to be in when it displays its document list and acts on the user’s choice of a document from the list. The _DocumentPicker superclass simply makes note of this logical value, leaving it to the subclasses to interpret it.

    The various _DocumentPicker’s FillDocumentArray( ) methods concentrate on different document types, and fill the array with the appropriate information for that type. Their ExecDocument( ) methods call different application object methods depending on their document type and the dialog box’s current state, sending information from the metatable from the array to method arguments as needed.

    The first two columns in the table below show you the names of these working classes and the document types that will appear in their lists, courtesy of their FillDocumentArray( ) method. The other columns show the application methods that call them, and the meaning assigned to their two states when ExecDocument( ) is triggered. Each application method listed here takes a logical parameter (defaulting to .F., State 1) to indicate for what purpose the class presents its document list.Expand table

    _DocumentPicker
    Subclass
    _Document typesAssociated _Application methodState 1
    action
    State 2
    action
    _NewOpenformsDoNewOpen( )EditAdd
    _ReportPickerreports and labelsDoReportPicker( )Run report/labelModify/Add not implemented
    in _Application superclass.
    _FavoritePickerdocuments and files of any typeDoStartupForm( )Run document/filePut document / file on Favorites menu for quick access.

    Appendix 4: Using the NEWAPPWIZ Visual Classes

    AppWizFormReinherit, the dialog box called by AppWizReinherit, and AppWizFormStandard, the default dialog box with the same interface as the original wizard, both descend from the same superclass, AppWizFormBaseBehavior (see Figure 11).

    Figure 11. Newappwiz.vcx in the Class Browser

    AppWizFormBaseBehavior is the required superclass for any dialog box provided as the UI of a NewAppWizBaseBehavior or its descendents. The Application Wizard superclass validates your dialog box class when it instantiates the dialog box as descending from this superclass dialog box.

    NewAppWizBaseBehavior contains only the very simple required behavior, no visible controls. It has three custom properties to represent required wizard information (project name, location, and whether or not the Wizard should generate project directory structure). It receives this information from an object reference the Wizard passes. It has a Finish( ) method which passes this information back to the Application Wizard.

    In your subclass of AppWizFormBaseBehavior, you simply databind the interface controls of your choice to these three custom properties. You create other controls and custom properties to represent your enhanced options. Your dialog box calls the Finish( ) method when you’re ready to generate. (Both AppWizFormReinherit and AppWizFormStandard use the OKButton class you see in Figure 11, which contains the call to its parent form’s Finish( ) method.)

    You can augment Finish( ) to pass more options from the dialog box back to your Wizard subclass as necessary.

    You’ll find more information in the NewAppWiz_Documentation method of the superclass. The default AppWizFormStandard subclass shows you a simple example of how to make it work.

    ADO Jumpstart for Microsoft Visual FoxPro Developers 

    • Article
    • 06/30/2006

    In this article

    1. Introduction
    2. What Are OLE DB and ADO?
    3. Why Incorporate ADO into a Visual FoxPro Application?
    4. ADO Object Model

    Show 2 more

    John V. Petersen

    April 1999

    Summary: Provides Microsoft Visual FoxPro developers with an overview of ActiveX Data Objects (ADO) and shows how to incorporate ADO into Visual FoxPro applications. Discusses the ADO object model and implementing Remote Data Services (RDS). (52 printed pages)

    Contents

    Introduction What are OLE DB and ADO? Why Incorporate ADO into a Visual FoxPro Application? ADO Object Model Remote Data Services Summary

    Introduction

    Microsoft®ActiveX® Data Objects (ADO) is perhaps the most exciting new Microsoft technology in quite some time. Because ADO is concerned with data, this new technology is of particular interest to Microsoft® Visual FoxPro® developers. Of course, you may ask, “Why do I need ADO? Visual FoxPro already has a high-performance local data engine.” It’s a good question.

    This paper provides the Visual FoxPro developer with a background of what ADO is and how to incorporate ADO into Visual FoxPro applications. After reading this paper, you should have enough information to readily answer the question: “Why do I need ADO?”

    A Brief Word About ADO Events

    One limitation of Visual FoxPro has been an inability to surface COM events. While Visual FoxPro can respond to events raised by ActiveX controls, objects created with the CreateObject function cannot. In Microsoft®Visual Basic®, COM Events are handled by using the WithEvents keyword. In Visual FoxPro, the new VFPCOM.DLL achieves the same results. The topics VFPCOM, ADO Events, and how to integrate ADO and Visual FoxPro will be discussed in another white paper. This paper is dedicated to providing the Visual FoxPro developer, with a comprehensive overview of ActiveX Data Objects, Remote Data Services (RDS), their respective objects, and how those objects work.

    This paper covers the following topics:

    • What are ADO and OLE DB?
    • Why incorporate ADO into a Visual FoxPro application?
    • The ADO object model
    • Remote Data Services

    What Are OLE DB and ADO?

    When discussing ADO, we are really talking about two distinct elements: the ActiveX data objects themselves and Microsoft Universal Data Access technology, more commonly known as OLE DB.

    OLE DB and Universal Data Access

    In simple terms, OLE DB is the succeeding technology to the Open Database Connectivity (ODBC) standard. OLE DB is a set of low-level interfaces that facilitate the Microsoft Universal Data Access strategy. ADO is a set of high-level interfaces for working with data.

    While both ODBC and OLE DB have the ability to make data available to a client, the capabilities of the two technologies are very different. ODBC is primarily designed for use on relational data. However, data exists in nonrelational as well as relational formats. In addition to new data formats, data resides in new places such as the Internet. Finally, the Microsoft Component Object Model (COM) framework requires better data access technology. Clearly, ODBC does not address these needs; a new technology is needed. That technology is OLE DB, and it is here to stay.

    The following graphic best illustrates how OLE DB and ADO work together. Clients can work directly with OLE DB or can work with OLE DB through the ADO interface (the latter is typically the case). Note that OLE DB can access SQL data either directly or through ODBC. An OLE DB provider provides direct access by OLE DB. Also note that OLE DB can also be used to access a variety of non-SQL data, as well as data that exists in mainframes. The ability to access data through a common interface, without regard to data location or structure, is the real power behind ADO and OLE DB.

    Whereas ODBC uses drivers, OLE DB uses providers. A provider is a software engine that provides a specific type of data that matches the OLE DB specification. Several OLE DB providers exist today, including those for Microsoft SQL Server™ and Oracle. Because there is such widespread use of ODBC, an OLE DB provider for ODBC has also been created in order to ease the migration from ODBC to OLE DB. Several nonrelational providers are currently under development. Perhaps the most anticipated of these is the OLE DB Provider for Microsoft Outlook®. A special provider, MS Remote, allows direct data access over the Internet. This brief list of providers shows the third-party community commitment to OLE DB, and many new providers are currently under development. For the latest news on available providers, refer to https://www.microsoft.com/data/.

    ADO Overview

    OLE DB is then a set of low-level interfaces that provide access to data in a variety of formats and locations. While powerful, OLE DB interfaces can be cumbersome to work with directly. Fortunately, ADO provides a set of high-level, developer-friendly interfaces that make working with OLE DB and universal data access a relatively simple task. Regardless of the programming environment you use, any Visual Studio® or Microsoft Office product such as Visual FoxPro, Visual Basic, Visual C++®, or Word, the interface you will use to access data remains constant. That interface is ADO, which in turn uses OLE DB.

    ADO itself is just a set of objects. By itself, ADO is not capable of anything. In order to provide any functionality, ADO needs the services of an OLE DB provider. The provider in turn uses the low-level OLE DB interface to access and work with data. One ADO connection may use a SQL Server OLE DB provider and another ADO connection may use an Oracle OLE DB provider. While the interface is constant, the capabilities may be very different because OLE DB providers are very different, which highlights the polymorphic nature of OLE DB.

    As developers, we crave consistency. ADO provides us with a consistent interface for our program code.

    ADO Version Summary

    The current version of ADO (2.1) is the fourth version of ADO to be released in less than two years. ADO 1.0 was primarily limited to working with Active Server pages. Only one OLE DB provider existed, the OLE DB Provider for ODBC Drivers.

    ADO (2.1)—Ships with the newest version of Microsoft Web browser, Internet Explorer 5.0. When discussing data or anything related to the Internet, it is almost impossible to do so without mentioning XML. XML, the Extensible Markup Language, is a mark-up language that allows users to create custom tags to describe data. XML is quickly becoming the universal format for storing and streaming data. The primary storage format in Office 2000 for document data will be XML. ADO (2.1) client-side recordsets can be saved as XML documents.

    ADO (2.0)—Represented a huge gain in functionality. One of the most notable new features was the ability to create client-side recordsets. To go along with this, also added were the abilities to create filters and indexes, and the ability to sort recordsets. These abilities are very much the same as those that exist with Visual FoxPro cursors. Finally, the ability to persist client-side recordsets was also added. In effect, data could be acquired from a server into a client-side recordset. The client-side recordset could then be saved as a file on the local hard-drive that could be opened at a later time without being connected to the network.

    ADO (1.5)—Introduced new capabilities and providers to ADO. Among the new providers was the OLE DB Provider for Jet (the JOLT Provider). The MS Remote Provider, which powers the Remote Data Services (RDS), was introduced as well. This version also introduced the ability to create disconnected recordsets.

    What You Need to Get Started

    In order to work through the examples presented in this paper, you will need the following:

    • Microsoft Visual FoxPro 6.0
    • Microsoft Data Access Components, which can be downloaded from https://www.microsoft.com/data/
    • SQL Server 6.5 or 7.0 with the sample Northwind database installed
    • A system DSN called TasTrade that points to the TasTrade Visual FoxPro Sample Database
    • A system DSN called Northwind that points to the SQL Server Northwind database

    Why Incorporate ADO into a Visual FoxPro Application?

    Have you ever wanted to pass a cursor as an argument to a function or class method? Or have you wanted to pass data to automation server applications such as Microsoft Word or Excel? Perhaps you have created a Visual FoxPro DLL and have needed a way to pass data from the user interface to a class method in the DLL. Maybe you have been looking for a way to stream data across the Web. If your answer is “yes” to at least one of these, ADO can help you today!

    Until now, the world of component-based development has lacked one thing: a method of effectively moving data between processes. Now, whether ADO is hosted by Visual FoxPro, Visual Basic, Excel, or Word, the interface is consistent. The new COM capabilities of Visual FoxPro 6.0 enable creating of ADO recordsets, populating them with data, and passing them to a variety of processes. This all goes to support the strategic positioning of Visual FoxPro, a creator of middle-tier components.

    Just about everything in Visual FoxPro is an object, except for reports, menus, and data. One of the biggest feature requests from Visual FoxPro developers has been the ability to work with data as a set of objects. Data objects provide several benefits, including an enhanced event model and the ability to overcome limitations of Visual FoxPro cursors. While many limitations are gone, many benefits of Visual FoxPro cursors have been retained. As you work with ADO, there’s good reason to think are many similarities to Visual FoxPro; ADO is based on the Visual FoxPro cursor engine. So, for those who have wanted data objects in Visual FoxPro, the wait is over with ADO.

    ADO is not a replacement for Visual FoxPro cursors. Rather, Visual FoxPro cursors and ADO are complementary. When used together, very powerful applications can result. The following pages detail the ADO object model and the common properties and methods you will work with, including:

    • Remote Data Services (RDS), technology which allows for the streaming of data over the Internet via HTTP.
    • VFPCOM.DLL, which enables the handling of COM events in Visual FoxPro.
    • ADO Integration into Visual FoxPro.

    This section has several comprehensive examples on strategies you may employ when integrating ADO into your Visual FoxPro Applications.

    ADO Object Model

    Connection Object

    ProgID: ADODB.Connection

    The purpose of the Connection object is to provide access to a data store. To illustrate, the following code creates an ADO Connection object:

    oConnection = CreateObject("adodb.connection")
    

    Once an ADO Connection object has been created, you can access its data store. An active connection can be established by providing a few pieces of key information and invoking the Open( ) method of the Connection object. The following code opens a connection to the Visual FoxPro TasTrade database:

    oConnection.Open("TasTrade")
    

    Alternatively, the following code accesses the SQL Server Northwind database:

    oConnection.Open("Northwind","sa","")
    

    These two examples work with the OLE DB Provider for ODBC drivers. Different OLE DB providers can be used as well. The following example sets some common properties of the Connection object and uses the OLE DB Provider for SQL Server:

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
          ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    

    The syntax of the ConnectionString property appears complicated. Fortunately, you don’t have to code this by hand. When you install the Microsoft Data Access Components (MDAC), you can create a data link file.

    To create a data link file:

    1. Right-click your desktop and choose New\Microsoft Data Link from the pop-up menu.
    2. Specify a name for the file.
    3. Right-click and select Properties to modify the file properties.
    4. In the Properties dialog box, click the Provider tab, and choose a provider.
      The OLE DB Provider for ODBC is the default choice. For this example, select the OLE DB Provider for SQL Server.
    5. Click the Connection tab.
    6. Specify the name of the server, your user name and password, and the name of the database you wish to connect to.
    7. Open the UDL file in Notepad.Now, it is just a matter of copying and pasting the information. Alternatively, you can use the file itself:oConnection.Open(“File Name=c:\temp\test.udl”)

    ADO recognizes four arguments in the ConnectionString:

    • File Name: Specifies the name of a UDL file to use.
    • Provider: Specifies the name of an OLE DB provider to use.
    • Remote Provider: Specifies the name of a provider to use with Remote Data Services (RDS).
    • Remote Server: Specifies the server on which data resides when using Remote Data Services (RDS).

    Any additional arguments passed in the ConnectionString are passed through to the OLE DB provider being used.

    In addition to the Open method, the following are the common methods you are likely to use with the Connection object:

    • BeginTransCommiTrans, and RollBackTrans—These methods work like the Begin Transaction, End Transaction, and RollBack statements in Visual FoxPro. The Connection object controls all transaction processing. For more detail, see the section Transactions/Updating Data. Note that not all OLE DB providers support transaction processing.
    • Close—This method closes an open Connection object.
    • Execute—This method runs a SQL statement, stored procedure, or OLE DB provider-specific command. In reality, a Command object, which actually does the work of executing the command, is created on the fly. More on the Command object and the flat object hierarchy of ADO later in this paper.
    • OpenSchema—This method returns information regarding defined tables, fields, catalogs, and views into an ADO Recordset object. This method works like the DBGetProp( ) function in Visual FoxPro.

    Errors collection

    ADO does not trap errors, nor does it have an error handler. Instead, ADO can record the occasions when errors occur. It is up to the host application, Visual FoxPro in this case, to both trap and handle the error. ADO only reports what errors have occurred. Note that the error is actually reported by the specific OLE DB provider. ADO is merely a vehicle to report the error.

    The Errors collection is part of the Connection object and consists of zero or more Error objects. When an error occurs, an Error object is appended to the Errors collection. The following code illustrates how the Errors collection works. In this example, the name of the database has been misspelled purposely in order to generate an error:

    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Nothwind;Data Source=JVP"
       .Open
    EndWith
    */ At this point an error will occur – causing VFP's default error
    */ handler – or the active error handler to invoke
    */ At this point, we can query the Errors Collection of the
    */ Connection Object
    For Each Error In oConnection.Errors
       ?Error.Description,Error.Number
    Next Error
    

    Recordset Object

    ProgID: ADODB.Recordset

    Once you establish an ADO connection, you can open a recordset of data. The Recordset object is very much like a Visual FoxPro cursor. Like the Visual FoxPro cursor, an ADO recordset consists of rows of data. The recordset is the primary object that you will use while working with ADO. Like the Connection object, the Recordset object also provides an Open method. To illustrate, the following code opens the Customer table of the Visual FoxPro Tastrade database:

    oRecordSet = CreateObject("adodb.recordset")
    oRecordSet.Open("Select * From Customer",oConnection)
    

    The first argument of the Open method specifies the source of data. As you will see, the source can take on several forms. The second argument of the Open method specifies a connection to use for retrieving the data specified by the source. At a minimum, this is all you need to open a recordset. Additional examples will expand on the additional arguments the Open method accepts.

    With a Recordset object created, one of the most common actions you will perform is navigating through records. Depending on the type of ADO recordset that has been created, certain navigational capabilities may or may not be available. The different types of possible ADO recordsets will be discussed shortly. The following code illustrates how to navigate through an ADO recordset:

    Do While !oRecordSet.Eof
       oRecordset.MoveNext
    EndDo
    

    The following paragraphs briefly describe the most common recordset properties and pethods you are likely to use. It is by no means a replacement for the ADO documentation, which gives both a complete description of the properties and methods and complete descriptions of acceptable enumerated types and arguments. ADO is well documented in the Microsoft Data Access Components (MDAC) SDK. You can download the MDAC SDK from https://www.microsoft.com/data.

    In addition, I highly recommend ADO 2.0 Programmers Reference, by David Sussman and Alex Homer, from Wrox Press.

    RecordSet types

    You can create four types of recordsets in ADO:

    • Forward Only—This type of recordset can be navigated only in a forward direction. It is ideal when only one pass through a recordset is required. Examples include populating a List box or a Combo box. The RecordCount property is irrelevant with this type of recordset.
    • Keyset—This type of recordset keeps acquired data up to date. For example, if you retrieve 100 records, data modified by other users to those 100 records will be visible in your recordset. However, modifications regarding new or deleted records made by other users will not be visible in your recordset. Both forward and backward navigation are supported. The RecordCount property returns a valid value with this type of recordset.
    • Dynamic—With this type of recordset, all underlying data is visible to the Recordset object. Because the number of records in the underlying table can change, the RecordCount property is irrelevant with this type of cursor. However, forward and backward navigation are supported.
    • Static—Both the number of records and data are fixed at the time the Recordset object is created. The only way to get the latest version of data and all records is to explicitly invoke the Requery method. You can use the RecordCount property. In addition, both forward and backward navigation is permitted.

    RecordSet locations

    Recordset objects can exist in either of two locations, the server or the client:

    • Server—The most common examples of server-side ADO recordsets are those created through Active Server Pages (ASP).
    • Client—A recordset that resides on a workstation is useful when creating disconnected recordsets or recordsets on which you wish to apply filters, sorts, or indexes.

    The most common properties you are likely to use with ADO recordsets include the following:

    • ActiveCommand property—An object reference to the Command object that created the recordset.
    • ActiveConnection property—An object reference, to the Connection object, that provides the link to an underlying data source.
    • AbsolutePosition property—Specifies the relative position of a record in an ADO recordset. Unlike the Bookmark property, which does not change, the AbsolutePosition property can change depending on the active sort and filter.
    • Bookmark property—A unique record identifier that, like the record number in a Visual FoxPro cursor or a record number in Visual FoxPro, does not change during the life of a recordset.
    • BOF/EOF properties—Beginning of File and End of File, respectively, that work just like the BOF( ) and EOF( ) functions in Visual FoxPro.
    • EditMode property—Specifies the editing state of the current record in an ADO recordset.
    • Filter property—The string that represents the current filter expression. This property is like the SET FILTER statement in Visual FoxPro. Unlike the Find method, multiple expressions linked with AND or OR operators are allowed. This property is only applicable to client-side recordsets.
    • Sort property—A comma-delimited set of fields that specifies how the rows in an ADO recordset are sorted. This property is only applicable to client-side recordsets.
    • State property—Specifies the state of an ADO recordset. Valid State properties are closed, open, connecting, executing, or fetching.
    • Status property—Specifies the editing status of the current record. Valid Status properties include unmodified, modified, new, and deleted. This property can be any one of the values contained in RecordStatusEnum.
    • MarshalOptions property—Specifies how records are returned (marshaled) to the server. Either all or only modified records can be returned. This property is only applicable to client-side disconnected recordsets
    • MaxRecords property—Specifies the total number of records to fetch from a data source.
    • RecordCount property—Specifies the number of records in a recordset. This property is like the Recc( ) function in Visual FoxPro.
    • Source property—Specifies the command or SQL statement that provides data for the recordset.

    Note   The type and location of a cursor as well as the OLE DB provider you select will affect the recordset properties that are available.

    Use the following table as a guide to help you make the right recordset type and location decision:

    Table 1. PropertiesExpand table

    TypeBookmarkRecordCountSortFilterMarshalOptions
    Forward Only     
    Key Set44   
    Dynamic     
    Static: Client44444
    Static: Server44   

    Only client-side recordsets can be sorted and filtered. If the CursorLocation property of ForwardOnlyKeySet, and Dynamic recordset types is set to adUseClient, making them client-side cursors, the CursorType property is automatically coerced to the Static Cursor type.

    Note   This is the behavior of the OLE DB Provider for SQL Server. The OLE DB Provider for ODBC supports only ForwardOnly and Static recordsets, regardless of where the recordset resides.

    As with properties, method availability can also vary:

    Table 2. Available MethodsExpand table

    TypeMoveFirstMovePreviousMoveNextMoveLastResyncRequery
    Forward Only  4  4
    Key Set4444 4
    Dynamic4444 4
    Static – Client444444
    Static – Server4444 4

    The following list describes some of the common methods you will use in the ADO Recordset object:

    • MoveFirstMovePreviousMoveNextMoveLast, and Move methods—Navigational methods that work as their respective names imply. The Move method accepts two arguments, the number of records to move and the position from which to begin the move. The Move method is similar to the Go statement in Visual FoxPro. MoveFirst and MoveLast work like Go Top and Go Bottom, respectively. Finally, MovePrevious and MoveNext work like Skip 1 and Skip –1, respectively.
    • Find method—Accepts a criterion string as an argument and searches the recordset for a match. If a match is not found, depending on the search direction, either the BOF or EOF property will evaluate to true (.T.). This method works much the same way as the Seek and Locate statements in Visual FoxPro. Unlike the Filter property and the Seek and Locate statements in Visual FoxPro, the ADO Recordset object does not allow multiple search values joined by the And or the Or operator. Using anything other than a single search value will result in an error.
    • Open method—Opens an existing ADO Recordset object. This method accepts several arguments and is discussed in detail later in this section.
    • Close method—Closes an ADO Recordset object. Many properties, such as CursorType and LockType, although read/write, cannot be modified while the recordset is open. The Close method must be invoked before those and other properties are modified.
    • Update and UpdateBatch methods—Update writes changes for the current record to the underlying data source; UpdateBatch writes pending changes for all modified records to the underlying data source. The UpdateBatch method is only relevant when Optimistic Batch Locking is used.
    • Cancel and CancelBatch methods—The Cancel method cancels modifications made to the current record; the CancelBatch method cancels pending changes to all modified records.
    • Resync method—Refreshes the Recordset object with data from the underlying data source. Invoking this method does not rerun the underlying command. Options exist for which records are actually refreshed.
    • Requery method—Unlike the Resync method, reruns the underlying command, which causes any pending changes to be lost. In effect, issuing a Requery is like invoking the Close method then immediately invoking the Open method.
    • Supports method—Specifies whether or not the recordset supports a function, based on a passed argument. For example, you can use this method to specify whether a recordset supports bookmarks, or the addition or deletion of records, or the FindUpdate, and UpdateBatch methods, to name a few. Because what is supported is depends on the OLE DB provider used, it is a good idea to use this method to make sure a needed function is supported.
    • GetRows method—Returns a set of records into an array.
    • GetString method—Returns a set of records into a string.

    The moral of the story is that before relying on the existence of anything in ADO, know and understand the OLE DB provider you are using, because the capabilities available to you can vary dramatically.

    Lock types

    There are four different locking schemes in ADO recordsets. These locking schemes are similar to those in Visual FoxPro.

    • Read-Only—As the name indicates, the recordset is opened for read-only purposes only. When you don’t need to modify data, this is the best locking scheme to use from a performance standpoint. This scheme applies to both server and client-side recordsets.
    • Lock Pessimistic—In this scheme, a lock attempt is attempted as soon as an edit is performed. This locking scheme is not relevant for client-side recordsets. Pessimistic Locking in an ADO recordset is like Pessimistic Locking with Row Buffering in a Visual FoxPro cursor.
    • Lock Optimistic—In this scheme, a lock attempt is made when the Update method is invoked. This locking scheme applies to both server and client-side recordsets. Optimistic Locking in an ADO recordset is like Optimistic Locking with Row Buffering in a Visual FoxPro cursor.
    • Lock Batch Optimistic—This scheme is like the Lock Optimistic scheme, except that more than one row of data is involved. In this scheme, a lock is attempted on modified records when the UpdateBatch method is invoked. This scheme is like Optimistic Locking with Table Buffering in a Visual FoxPro cursor.

    The following table illustrates the availability of some common methods depending on the locking scheme used:

    Table 3. Method Availability (Depending on Lock Type)Expand table

    Lock TypeCancelCancelBatchUpdateUpdateBatch
    Read Only4   
    Pessimistic4444
    Optimistic4444
    Optimistic Batch4444

    With the concepts of cursor types, locations, and locking schemes out of the way, we can discuss the real abilities of ADO recordsets. The most notable of these abilities are updating, sorting, and filtering of data. Before undertaking that discussion, however, take a few moments to review the Fields Collection object.

    Fields collection object

    Associated with the Recordset object, is the Fields Collection object. The Fields Collection object contains zero or more Field objects. The following code enumerates through the Fields Collection of a Recordset object:

    For Each ofield In oRecordset.Fields
       With oField
          ?.Name,.Value,.Type,.DefinedSize
          ?.ActualSize,.NumericScale,.Precision
       EndWith
    Next oField
    

    The common Field properties you will work with:

    • Name—Specifies the name of the Field object. This corresponds to the name of the data element in the underlying data source. It is easy to define the name element as the name of the field in the underlying table. However, note that ADO and OLE DB work with both relational and nonrelational data. Given that, while you may be working with ADO, the underlying data may come from Outlook, Excel, Word, or Microsoft® Windows NT® Directory Services.
    • Value—Indicates the current value of the Field object.
    • OriginalValue—Indicates the Value property of the Field object before any modifications where made. The OriginalValue property returns the same value that would be returned by the OldVal( ) function in Visual FoxPro. When you invoke the Cancel or CancelUpdate methods of the Recordset object, the Value property of the Field object is replaced by the contents of the OriginalValue property. This behavior is similar to that exhibited when TableRevert( ) is issued against a Visual FoxPro cursor.
    • UnderlyingValue—Indicates the current value in the data source. This property corresponds most closely to the CurVal( ) function in Visual FoxPro. To populate the Value property of each Field object in the Fields collection, you need to invoke the Resync method of the Recordset object. With a client-side cursor, this property will return the same value as the OriginalValue property, since the recordset may or may not have an active connection.
    • Type—Indicates the data type of the Field object. The value of this property corresponds to a value contained in DataTypeEnum. Examples of values in DataTypeEnum are adBoolean, adInteger, and adVarChar.
    • Defined Size—Specifies the size of the field containing a data element in the data source. For example, in SQL Server, the Country field in the Customers table of the Northwind database is 15 characters long. Therefore, the DefinedSize property of the Country Field object is 15.
    • ActualSize—Represents the length of the actual data element in a datasource. To illustrate, consider the Country Field object again. In the case where the value is Germany, the ActualSize property is 7, while the DefinedSize property is still 15.
    • NumericScale—Specifies how many digits to the right of the decimal place are stored.
    • Precision—Specifies the maximum number of digits to be used for numeric values.

    In addition to these properties, GetChunk is one interesting method you are likely to use. This method allows you to progressively fetch portions of the contents of a field object. This method is very useful when dealing with large text fields. It can be used only on fields where the adFldLong Bit set of the Attributes property is set to true (.T.). See the next section for details on the Attributes property. Understand that fields of the type ADLongVarChar have the adFldLong Bit set. The Notes field of the Employees table is of the type adLongVarChar.

    The following code fetches data from the notes field in 10-byte chunks:

    Local nBytesRead,cChunkRead 
    nBytesRead = 0
    cChunkRead = Space(0)
    Do While .T.
       nBytesRead = nBytesRead + 10
       cChunkRead = oRecordset.Fields("notes").GetChunk(10)
       If IsNull(cChunkRead) Or;
        nBytesRead > oRecordset.Fields("notes").ActualSize
          Exit
       Else
          ?cChunkRead
       Endif   
    EndDo
    

    Successive calls to GetChunk continue where the previous call ended. The GetChunk method is very useful when you need to stream data or only need to see the first few characters of a large text field.

    Along with GetChunk, examine the AppendChunk method. The first time this method is called for a field, it overwrites any data in the field. Successive calls then append the data, until pending edits are cancelled or updated. The following code illustrates how this method works:

    For x = 1 To 100
       oRecordset.Fields("notes").AppendChunk(Str(x)+Chr(10)+Chr(13))
    Next x
    

    Both the GetChunk and AppendChunk methods are ideal for dealing with low memory scenarios.

    The Attributes property

    An attribute specifies the characteristics of something. As a person, you have many attributes, eye color, height, weight, and so forth. In the OOP world, objects have many attributes. Most of the time, attributes are exposed in the form of properties. A Visual FoxPro form has several properties such as WidthHeight, and BackColor, just to name a few. The same is true for objects in ADO. Sometimes, however, it is not convenient to have a one-to-one correspondence between attributes and properties. Often, you can pack large amounts of information into a smaller space through the power of setting bits. A bit is much like a switch. It is either on or off or 1 or 0. If you string these bits together, you gain the ability to store multiple values in a small space. This is how the Attributes property works.

    The ConnectionParameterField, and Property objects all have an Attributes property. If you have never worked with bit operations before, working with this property can be quite challenging. In some situations, as is the case with the GetChunk and AppendChunk methods, you will need to refer to the Attributes property of the Field object to determine whether those methods are available.

    Using the Field object to illustrate how the Attributes property works, you can associate the following attributes with a Field object and its associated binary values:

    • AdFldMayDefer—Indicates that the field contents are retrieved only when referenced—0x00000002
    • adFldupdateable—Indicates that the field can be updated—0x00000004
    • adFldUnkownupdateable—Indicates that the provider does not know whether the field is updateable—0x00000008
    • adFldFixed—Indicates that the field contains fixed length data—0x00000010
    • adFldIsNullable—Indicates that the field can accept a null value during a write operation—0x00000020
    • adFldMayBeNullable—Indiates that the field may contain a null value—0x00000040
    • adFldlong—Indicates that the field contains long binary data and that the GetChunk and AppendChunk methods can be used—0x00000080
    • adFldRowID—Indicates that the field contains a row ID and cannot be updated. This does not relate to a field that may contain the identity value or some other auto-incrementing value. Rather, it relates to a ROW ID that is unique across the database. Oracle has this feature—0x00000100
    • adFldRowVersion—Indicates whether the field indicates the version of the row. For example, a SQL TimeStamp field may have this attribute set—0x00000200
    • adFldCachedDeferred—Indicates that once this field has been read, future references will be read from the cache—0x00001000

    Usually, more than one of these attributes are present at any given time. Yet the Attributes property is a single value. Using the Employees table Notes field as an example, you will see that the Attributes property yields a value of 234. The value 234 represents the sum of the attributes for that field. For example, nullable and long attributes have decimal values of 32 and 128 respectively. This means that the Attributes property evaluates to 160. This works like the Windows Messagebox dialog box with regard to specifying the icon and types of buttons that are present.

    Knowing that the Attributes property is a sum of the attributes of a Field object does not help in determining whether a specific attribute is present. This is where understanding bit operations comes in handy. The first step is to convert the sum (such as 234, above) into a binary equivalent:

    11101010
    

    Working from right to left, (or from the least significant bit to the most significant)—and beginning with zero, see that bits 1, 3, 5, 6, and 7 are set, (indicated by their values of 1 in those positions). Bits 0, 2, and 4 are not set. The next step is to determine whether a field is “long.”

    To determine whether a field is a long field, we must first convert the adFldLong constant, which specifies which bit if set, indicates that the field is long. The adFldLong constant has a hex value of 0x00000080. This translates into a decimal value of 128. The following is the binary equivalent:

    10000000
    

    Converting a hex value to decimal in Visual FoxPro is simple. The following code illustrate how to convert hexadecimal values to decimal:

    x = 0x00000080
    ?x && 128
    

    And, if you ever need to convert back to hexadecimal:

    ?Transform(128,"@0") && 0x00000080 
    

    Using our original hex value, 11101010, and working from right to left and beginning with zero, see that the seventh bit is set. Therefore, the seventh bit of the Attributes property, if set, means the field is long. Going further, whatever attributes occupy bits 1, 3, 5, and 6, also apply to this field. The following table of field attributes should help to sort things out:

    Table 4. Field AttributesExpand table

    Hex ValueDecimal ValueField Attribute ConstantBit
    0x000000022AdFldMayDefer1
    0x000000044AdFldupdateable2
    0x000000088AdFldUnkownUpdateable3
    0x0000001016AdFldFixed4
    0x0000002032AdFldIsNullable5
    0x0000004064AdFldMayBeNull6
    0x00000080128AdFldLong7
    0x00000100256AdFldRowID8
    0x00000200512AdFldRowVersion9
    0x000010004096AdFldCacheDeferred12

    So, along with being a long field, the field is deferred, updateable, can have a null written to it, and it may also already contain a null value. Visually, this makes sense. How can you do this programmatically?

    If you refer to online examples (almost always programmed in Visual Basic), you will see code like this:

    If (oField.Attribute AND adFldLong) = adFldLong 
       ' The field is long
    End If
    

    This is pretty slick in that you can test for whether a specific attribute bit is set by using the AND operator with the attribute property and the constant. If you try this in Visual FoxPro, you will get data type mismatch errors. Fortunately, there is a way. Visual FoxPro contains a host of bit functions. One function, BITTEST, does as its name implies. It tests whether a specified bit in a passed argument is set. To review, we need to see if the seventh bit in the value 234 is set. The following Visual FoxPro code demonstrates how to use the BITTEST function:

    If BitTest(234,7)
       */ The Field is long
    Endif
    

    To find out if the field is nullable:

    If BitTest(234,5)
       */ The Field is long
    Endif
    

    The Attributes property of the ConnectionParameter, and Property objects works in the same manner as illustrated above. The differences are the names and quantity of attributes that are present.

    ADO and COM defined constants

    ADO and OLE DB, like any COM components, make extensive use of defined constants in the examples that document the usage of properties, events, and methods. Other development environments in Visual Studio such as Visual Basic and Visual Interdev provide IntelliSense technology, because of their respective abilities to interact directly with the type libraries of COM components. For these development environments, you can reference defined constants just as if they were a part of the native language. So, working with published examples is a fairly trivial task. On the other hand, in the Visual FoxPro development environment there is, in fact, a bit of a challenge. The question always seems to be “How can I use the Visual Basic samples in Visual FoxPro?” The biggest stumbling block is usually in finding the value of the defined constants. In Visual FoxPro, you need to use the #Define statement for each constant.

    One solution for obtaining the value of the ADO defined constants is to obtain the MDAC SDK from Microsoft. The MDAC SDK can be downloaded from https://www.microsoft.com/data/download.htm.

    Once you install the SDK, locate the Include\ADO directory. In that directory, you will find the ADOINT.H file, which contains all of the enumerated types and the values for the defined constants.

    A second, and perhaps easier, solution is to use the resources already installed on your machine. If you are working through the sample code in this paper, you already have the Microsoft Data Access Components installed on your workstation. The Visual Basic Development Environment (both the full Visual Basic IDE and the Visual Basic Editor in desktop applications like Word and Excel) has a great resource called the Object Browser. This could, in fact, be the most underutilized tool on the planet.

    To illustrate its functionality, open any desktop application that uses Visual Basic, such as Word or Excel. Or, if you have the Visual Basic Programming System installed, you can open that as well.

    If you opened a VBA application

    1. From the View menu, choose Toolbars.
    2. From the Toolbars menu, choose Visual Basic.
    3. On the Visual Basic toolbar, click Visual Basic Editor.
    4. From the Tools menu, choose References.
    5. Check the Microsoft Data Access Objects 2.x Library.

    If you opened the Visual Basic IDE

    1. Create an empty project.
    2. From the Project menu, select References.
    3. Check the Microsoft Data Access Objects 2.x Library.

    Now, whether you are in the VBA Editor or the VB IDE

    1. Press F2 to display the Object Browser.
    2. In the first combo box, select ADODB.
    3. In the second box, type ADVARCHAR.
    4. Press Search
      or
      Press Enter.

    Clearly, the Object Browser is a powerful tool for the developer who works with COM components. Not only are the defined properties, events, and methods accessible in the Object Browser, so also are the defined constants and their respective values. Notice the value of adVarChar in the lower pane of the Object Browser.

    Opening, sorting, and filtering data

    One of the big advantages of using a development platform such as Visual FoxPro is its local data engine. Not only does the engine provide superior query performance, but it also provides some very flexible capabilities when it comes to both working with and presenting data. There isn’t a Visual FoxPro application that fails to sort or filter data to some degree. In Visual FoxPro, sorting is accomplished by creating a set of index tags for a table. Filtering is accomplished by using the Set Filter command. Fortunately, ADO has these capabilities as well.

    You can see in the Field Attribute table that the availability of features depends on the location in which the recordset is created. It is clear that we must ensure that a client-side recordset is created.

    For example, create a Connection object to the TasTrade or SQL Server Northwind database. The following code assumes that the Connection object, oConnection, has been created before you open the Recordset object.

    First, we need to implement a few required #Defines:

    #Define adUseClient   3
    #Define adLockBatchOptimistic   4
    #Define adCmdTable   2
    

    For SQL Server:

    With oRecordset
       .Source = "Customers"
       .ActiveConnection = oConnection
       .CursorLocation = adUseClient
       .LockType = adLockBatchOptimistic
       .Open
    EndWith
    

    Or

    oRecordset.Open("Customers",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic)
    

    For Visual FoxPro:

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Customer"
       .CursorType = adOpenStatic
       .LockType = adLockReadOnly
       .CursorLocation = adUseClient
       .Open(,,,,adCmdtable)
    EndWith
    

    Or

    oRecordset.Open("Customer",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic,;
                     adCmdTable)
    

    Or

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Select * From Customer"
       .CursorType = adOpenStatic
       .LockType = adLockReadOnly
       .CursorLocation = adUseClient
       .Open
    EndWith
    

    Or

    oRecordset.Open("Select * From Customer",;
                     oConnection,;
                     adUseClient,;
                     adLockBatchOptimistic)
    

    SQL Server and Visual FoxPro open data differently. Remember that when using SQL Server, you are using the OLE DB Provider for SQL Server. When you access data in Visual FoxPro, use the OLE DB Provider for ODBC, since there is no native OLE DB provider for Visual FoxPro.

    The difference rests with the optional fifth argument of the Open method. The SQL Server OLE DB Provider is designed to recognize when you pass just a table name. With the ODBC OLE DB Provider, you must specify how it should interpret the Source property. By default, the ODBC OLE DB Provider expects a SQL statement. When you pass a SQL statement, there is no need to explicitly state how the provider should interpret things. The Visual FoxPro ODBC driver generates an “Unrecognized Command Verb” error message if you only specify a table name as the source and you fail to use the optional fifth argument. Note that if you use the ODBC OLE DB Provider to access SQL Server, you must employ the same technique that is needed for Visual FoxPro.

    Which method should you employ when you populate the properties individually before invoking the Open method or passing the arguments to the Open method? Once again, it is a matter of preference. Of the two, manually populating the properties makes for more readable code.

    Sorting and filtering data are just matters of manipulating the Sort and Filter properties respectively. The following code sorts the recordset created from TasTrade in the example above, by country, ascending, then by region, descending:

    oRecordset.Sort = "Country,Region Desc"
    

    The following code displays the sort and the functionality of the AbsolutePosition and Bookmark properties.

    oRecordset.MoveFirst
    Do While Not oRecordset.Eof
       With oRecordset
          ?.Fields("country").Value,;
           .Fields("region").Value,;
           .AbsolutePosition,;
           .Bookmark 
           .MoveNext
       EndWith
    EndDo
    

    Setting a filter is as easy as setting the sort. The following code filters for records where the country is Germany:

    oRecordset.Filter = "Country = 'Germany'"
    

    The Filter property also supports multiple values:

    oRecordset.Filter = "Country = 'Germany' Or Country = 'Mexico'"
    

    Finally, wild card characters are also supported:

    oRecordset.Filter = "Country Like 'U*'"
    

    To reset either the Filter or Sort properties, set them equal to an empty string:

    oRecordset.Sort = ""
    oRecordset.Filter = ""
    

    Finding data

    Another important capability of an ADO recordset is the ability to find records based on a search string. This capability works like searching for records in a Visual FoxPro cursor. Unlike the Seek or Locate statement in Visual FoxPro, the Find method provides control over the scope of records that are searched. The following code searches for a country that begins with the letter “B.”

    oRecordset.Find("country Like 'B%'")
    

    Although multiple criteria are not allowed, wild card searches are permitted:

    oRecordset.Find("country Like 'U*'")
    

    Searches for multiple criteria, such as the following, would result in an error:

    oRecordset.Find("country Like 'G*' Or country Like 'B*'")
    

    Transactions/updating data/conflict resolution

    Updating data in an ADO recordset is a fairly simple process. As in any environment, conflict resolution in multi-user environments is always an issue to be dealt with. This is where the Errors collection comes into play. Error trapping and handling needs to become an integral part of your ADO-related code. The following code samples employ a simple error handling scenario and use the Errors collection to determine whether conflicts have occurred. For a complete list and description of ADO error codes, consult the online documentation.

    When you update data, you can update either a single row, or several rows at a time in batch mode. These methods most closely correspond to row and table buffering, respectively, in Visual FoxPro. Building on the recordset already created, the lock type is Batch Optimistic. While updates are normally conducted in batches, you can also update one row at a time, just as in Visual FoxPro.

    The following code modifies the CompanyName field and attempts to update the SQL Server data source:

    oRecordset.Fields("companyname").Value = "Ace Tomato Company"
    oRecordset.Update
    

    Depending on a variety of scenarios, this code may or may not work. Perhaps a contention issue exists? Perhaps the user does not have rights to modify data. Hundreds of issues can cause an attempted update to fail. Therefore, anytime you attempt an update, you should employ error trapping. The following code expands the previous example and makes it a bit more robust:

    Local Err,cOldErr,oError
    cOldError = On("Error")
    On Error Err = .T.
    oRecordset.Fields("companyname").Value = "Ace Tomato Company"
    oRecordset.Update
    If Err
       For Each oError In oRecordset.ActiveConnection.Errors
          With oError
             ?.Number,.Description
          EndWith
       Next oError
    Endif
    On Error &cOldErr
    

    If you are thinking, “Hey, maybe I should write a wrapper class to better encapsulate and centralize code,” you’re on the right track. The following code creates a custom class that can serve as a starting point:

    Local oRecordsetHandler
    oRecordsetHandler = CreateObject("RecordsetHandler")
    oRecordset.Fields("companyname").Value = "Alfreds Futterkiste"
    If !oRecordsetHandler.Update(oRecordset)
       oRecordsetHandler.Cancel(oRecordset)
    Endif
    Define Class RecordsetHandler As Custom
       Protected oRecordset
       Protected ErrFlag
       
       Procedure Update(oRecordset)
          This.oRecordset = oRecordset
          oRecordset.UpdateBatch
          Return !This.ErrFlag
       EndProc
       Procedure Cancel(oRecordset)
          This.oRecordset = oRecordset
          oRecordset.Cancel
          Return !This.ErrFlag
       EndProc
       
       Procedure Error(nError, cMethod, nLine)
          Local oError
          For Each oError In This.oRecordset.ActiveConnection.Errors
             With oError
                ?.Number,.Description
             EndWith
          Next oError
          This.ErrFlag = .T.
       EndProc
    EndDefine
    

    There’s a better way to determine whether an update proceeded successfully. The preferred approach is to trap events that ADO fires. Visual FoxPro by itself does not surface COM Events. Fortunately, the new VFPCOM.DLL component provides this capability to Visual FoxPro. The previous example can be modified to show how using COM Events makes for more robust code and class design.

    Now we can improve the code of our example. Most of the time, for efficiency, you will want to batch your updates that comprise multiple records. Often, when you update multiple records, transaction processing is required. In other words, either updates to all records must succeed or none should occur. To illustrate, let’s say you must apply a 10 percent price increase to the products you sell. The prime requirement is that all records in the Products table need modification. Without transactional capabilities, the possibility exists that, for example, after the first 10 records are updated, an error generated on the eleventh record prevents a complete update. Transaction processing provides the ability to rollback changes.

    The following example incorporates error trapping and the three transaction methods of the Connection object:

    Local Err,cOldErr
    cOldErr = On("error")
    On Error Err = .T.
    oRecordset.ActiveConnection.BeginTrans
    Do While !oRecordset.Eof
       If Err
          Exit
       Else
          With oRecordset
             .Fields("unitprice").Value = ;
                .Fields("unitprice").Value * 1.1
             .Movenext
          EndWith   
       Endif
    EndDo
    oRecordSet.UpdateBatch
    If Err
       oRecordset.ActiveConnection.RollBackTrans
       oRecordset.CancelBatch
    Else
       oRecordset.ActiveConnection.CommitTrans
    Endif   
    On Error &cOldErr 
    

    Additional operations you are likely to employ with recordsets deal with adding new records and deleting existing records. Both of these processes are very simple. The following code adds a new record:

    oRecordset.AddNew
    

    As in Visual FoxPro, in ADO the new record becomes current. Once the AddNew method is invoked, the field can be populated and, depending on the LockType, you then invoke either the Update or UpdateBatch methods to modify the data source.

    Deleting records is just as easy. The following code deletes the current record:

    oRecordset.Delete
    

    Once again, after deleting the record, a call to Update or UpdateBatch will update the data source.

    SQL Server identity fields and parent/child relationships

    SQL Server, like most server RDBMSs and Microsoft® Access®, creates an auto-incrementing field that can serve as a primary key for a table. Typically, the data type for this field is Integer. In SQL Server, this type of field is called the Identity field. Fields of this type are read-only. It begs the question, “When adding records, how can one determine what these values are?” Knowing that the next generated value is a requirement for maintaining referential integrity when child tables are involved. The following example code shows a recordset in which the first field, ID, is the auto-incrementing field. After new field is added, checking the value of the ID field yields a character with a length of zero. Attempting to update the field results in an error. However, once the recordset is updated, checking the value again will yield a valid identity value.

    oRecordset.AddNew
    ?oRecordset.Fields("id").Value && empty string
    oRecordset.UpdateBatch
    ?oRecordset.Fields("id").Value && returns new identity value
    

    With the new identity value available, you can add records in child tables, using the identity value in the parent table as the foreign key in the child tables.

    But, what do you do in cases where you have disconnected recordsets?

    This section details an important capability in ADO—the ability to have recordsets without an active connection to the backend data source. At this point you can freely add new records to disconnected records. When the recordset is eventually reconnected, those newly added records are then sent to the backend data source. How do you know what the identity value will be in those cases? Simply put, you don’t know. At the same time, however, you still need to be able to add both parent and child records locally. You need some method that maintains the relationship locally, while at the same time, supporting the use of the identity value when the data is sent to the backend.

    The simplest solution to this problem is to include a field in each table that serves as the local ID. You need this extra field because the identity field will be read-only. On the client side, you can use several methods for producing an ID that is unique. One approach is to use the Windows API to fetch the next Global Unique Identifier (GUID). The following procedure outlines how the local process unfolds:

    1. Add a new parent record.
    2. Fetch the next GUID.
    3. Update the local primary key column with the GUID.
    4. Add a new child record.
    5. Update the local primary key column with the GUID.
    6. Update the foreign key column of the child with the GUID from its parent.

    At some point, you will reconnect to the server. The update process could be performed within the context of a transaction, done one row at a time by navigating through each record. Checking the recordset Status property, which indicates whether the current record has been newly created, modified, deleted, and so on, determines whether the current row should be sent back to the server. If the record should be sent back, the parent record can be updated via the UpdateBatch method. The UpdateBatch method accepts an optional argument that specifies that only the current record be updated. By default, UpdateBatch works on all records. If the value of one is passed—corresponding to the adAffectCurrent constant—only the current record is updated. Once the update occurs, the identity value generated by the server is available. This value would then be used to update the foreign key columns of any related children. Once that process is complete, the records for that parent would be sent back to the server as well. This same process would be used if grandchild and great-grandchild relationships also existed.

    The following Visual FoxPro code, from Visual FoxPro 6 Enterprise Development, by Rod Paddock, John V. Petersen, and Ron Talmage (Prima Publishing), illustrates how to generate a GUID:

    Local oGuid
    oGuid = CreateObject("guid")
    ?oGuid.GetNextGuid( )
    */ Class Definition
    Define Class guid AS Custom
      */ Create protected members to hold parts of GUID
      Protected data1
      Protected data2
      Protected data3
      Protected data4
      Procedure GetNextGuid
        */ The only public member. This method will return the next GUID
        Local cGuid
        cGuid = This.Export( )
        UuidCreate(@cGuid)         
        This.Import(cGuid)            
        cGuid = This.Convert(cGuid)
        Return cGuid
      EndProc
      Protected Procedure bintoHex(cBin)
        */ This method converts a binary value to Char by calling the Hextochar
    
        */ Method
        Local cChars, nBin
        cChars = ""
        For nDigit = 1 To Len(cBin)
          nBin = Asc(Substr(cBin, nDigit, 1))
          cChars = cChars + This.Hex2Char(Int(nBin/16)) + ;
            This.Hex2Char(Mod(nBin,16))
        EndFor
        Return(cChars)
      EndProc
      Protected Procedure hex2char(nHex)
        */ This method converts a hex value to  ASCII 
        Local nAsc
        Do Case
          Case Between(nHex,0,9)
            nAsc = 48 + nHex
          Case Between(nHex,10,15)
            nAsc = 65 + nHex - 10
        EndCase
        Return(Chr(nAsc))
      EndProc
      Procedure import(cString)
        */ This method takes the binary string and populates the 4 data
        */ properties
        With This
          .Data1 = Left(cString, Len(.Data1))
          cString = SubStr(cString, Len(.Data1)+1)
          .Data2 = Left(cString, Len(.Data2))
          cString = SubStr(cString, Len(.Data2)+1)
          .Data3 = Left(cString, Len(.Data3))
          cString = SubStr(cString, Len(.Data3)+1)
          .Data4 = Left(cString, Len(.Data4))
        EndWith
        Return cString
        EndProc
    
      Protected Procedure export
        */ This method creates the buffer to pass to the GUID API.
        With This
          .Data1 = Space(4)
          .Data2 = Space(2)
          .Data3 = Space(2)
          .Data4 = Space(8)
        EndWith
        Return(This.Data1 + This.Data2 + This.Data3 + This.Data4)
      EndProc
      Protected Procedure Convert(cGuid)
        */ This method makes the call to the BinToHex that 
        */ converts the data in the 4 data properties from 
        With This
          cGuid =  .BinToHex(.Data1) + "-" + .BinToHex(.Data2) + "-" + ;
            .BinToHex(.Data3) + "-" + .BinToHex(.Data4)
          Return cGuid
        Endwith 
        EndProc
      Procedure Init
        */ Declare the function in the DLL
        Declare Integer UuidCreate ;
          In C:\Winnt\System32\RPCRT4.DLL String @ UUID
        Return
      EndProc
    EndDefine
    

    Output is produced as follows:

    Disconnected/Persisted Recordsets

    One of the most powerful features of ADO is the ability to create both disconnected and persisted recordsets. A disconnected recordset is a client-side recordset that does not have a current ActiveConnection. SQL data sources, such as SQL Server, Oracle, and so on, are licensed according to the number of concurrent connections. For example, the number of people that using an application connected to SQL Server is 300. However, it has been determined that at any time, only 50 users actually use the services of a connection. A connection is needed only when data is being requested, updates are made, or a stored procedure on the database server is invoked. From a financial standpoint, it is far less expensive for a company to only purchase 50 licenses than to purchase 300. From a resource standpoint, performance should improve because the server only has the overhead of 50 connections instead of 300, of which 250 are idle at any time.

    Using the ADO recordset of customer data already created, the following code disconnects the client-side recordset:

    oRecordSet.ActiveConnection = Null
    

    If you attempt to do this with a server-side recordset, an error occurs stating that the operation is not allowed on an open recordset. Once the recordset is disconnected, you can continue to work with and modify records. The following code will work:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       ?oRecordset.Fields("companyname").Value
       oRecordset.Fields("companyname").Value = ;
          Upper(oRecordset.Fields("companyname").Value)
       oRecordset.MoveNext 
    EndDo
    

    With modified records in a client-side recordset, three basic options exist.

    • Cancel local changes
    • Marshall local changes to the server
    • Save (persist) the recordset locally.

    You can save (persist) the recordset locally for both later use and, ultimately, for marshalling those persisted changes back to the server.

    The first choice is pretty simple to implement, since it takes one line of code:

    oRecordset.CancelBatch 
    

    The second choice is also simple to implement. Much of the work in updating multiple records and transactions has already been detailed. This procedure really involves two separate steps:

    1. Re-establish an active connection.
    2. Marshal modified records back to the data source.

    The following code re-establishes the connection:

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
          ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    oRecordset.Activeconnection = oConnection
    

    Then the code marshals the records by attempting the updates

    Local Err,cOldErr
    cOldErr = On("error")
    On Error Err = .T.
    With oRecordset
       .ActiveConnection.BeginTrans
       .UpdateBatch
       If Err
          .ActiveConnection.RollBackTrans
          .CancelBatch
       Else
          .ActiveConnection.CommitTrans
       Endif
    EndWith   
       On Error &cOldErr
    

    Often, however, there’s a need to shut things down and then reopen the recordset at another time. To be effective, the recordset must reflect incremental changes. This cycle may repeat any number of times.

    To illustrate how to persist a recordset, consider again the following code that modifies records in a Recordset object:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       ?oRecordset.Fields("companyname").Value
       oRecordset.Fields("companyname").Value = ;
          Upper(oRecordset.Fields("companyname").Value)
       oRecordset.MoveNext 
    EndDo
    

    Now you can invoke the Save method to persist the recordset:

    oRecordset.Save("c:\temp\customers.rs")
    

    At a later time, you can open the persisted recordset:

    oRecordset = CreateObject("adodb.recordset")
    oRecordset.Open("c:\temp\customers.rs")
    

    After the persisted recordset is reopened, you can use the same code, which establishes a connection to a disconnected recordset, to make additional modifications. You can marshal changes made in the persisted recordset to the underlying data source.

    Hierarchical/Shaped Recordsets

    Visual FoxPro not only provides the ability to work with local data, but also the ability to set up relations using the Set Relation command. When you move the record pointer in the parent table, the record pointer automatically moves in any child tables that exist. This makes working with and building interfaces for one to many relationships very simple in Visual FoxPro. Fortunately, the same capability exists in ADO, in the form of hierarchical recordsets, also referred to as shaped recordsets.

    There are two necessary components when creating and working with hierarchical recordsets:

    • The Microsoft DataShape OLE DB Provider, MSDataShape
    • The Shape language, a superset of the SQL syntax

    The first requirement is fairly easy to fulfill because it only entails setting the Provider property of the ADO Connection object to the proper value:

    oConnection.Provider = "MSDataShape"
    

    The second requirement, using the Data Shape language, is a bit more challenging. When you first see Data Shape language, it can be fairly intimidating, just as FoxPro may have been when you first worked with it. But like anything else, with a bit of practice and patience, Microsoft Data Shape language will become second nature.

    To examine Shape language, consider a parent-child common scenario of customers and orders. For each customer, zero or more orders can exist. In turn, each order can contain one or more line items. The following code employs Shape syntax to relate customers and orders in the SQL Server Northwind database:

    SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND ({SELECT * 
    FROM "dbo"."Orders"} AS Orders RELATE "CustomerID" TO "CustomerID") AS 
    Orders
    

    If your first thought is, “Gee, this is like setting relations in Visual FoxPro,” you are indeed correct. It is exactly the same principle. If the Shape syntax is broken down, the task becomes manageable. The first clause in the code begins with the keyword SHAPE, to signify that what follows is not pure SQL, but rather, Data Shape language. The Data Shape language is a super-set of SQL, which is why you need to use MSDataShape as the OLE DB provider. MSDataShape can interpret and execute Shape commands. Finally, the last portion of the first command specifies that the results of the SQL statement are to be aliased as Customers.

    In the next set of commands, things get a bit complicated, especially when the hierarchy is nested an additional one or two levels (this is the case when order details are added, as we’ll do in the next example).

    You can interpret the keyword APPEND as “Append the results of the next SQL statement to the results of the previous SQL statement.” Of course, just appending records won’t suffice. Rather, you must provide a rule that specifies how the records are to be related. This is where the RELATE keyword comes into play.

    You can interpret the RELATE keyword as, “When appending records, do so based on these join fields.” In this case, the join is between the CustomerID column in the Customers table and the CustomerID column in the Orders table.

    Finally, we need to alias the data that was just appended as Orders. The following code sets up the objects and creates the hierarchical recordset:

    #Include adovfp.h
    Local oRecordset,oConnection,oCommand, cShpStr
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers '
    cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Orders"} ;
      AS  Orders '
    cShpStr = cShpStr + 'RELATE "CustomerID" TO "CustomerID") AS Orders'
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security ;
        Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    

    The question at this point is, “How is the data appended?” The technique is rather clever. When you append a recordset to another recordset, you do so through a Field object. If you query the Count property of the Fields collection, you discover that the value of 12 is returned. However, in SQL Server, you see that the Customers table only has 11 fields. The twelfth field, in this case, is actually a pointer to the Orders recordset. The rows in the Orders recordset for a given row in the Customers recordset are only those for that customer. The following code illustrates just how powerful hierarchical recordsets are:

    oRecordset.MoveFirst
    Do While !oRecordset.Eof
       With oRecordset
          ?.Fields("Customerid").Value,.Fields("CompanyName").Value
       EndWith
       oOrders = oRecordset.Fields("orders").Value
       Do While !oOrders.Eof
          With oOrders
          ?Chr(9),.Fields("Customerid").Value,.Fields("orderdate").Value
          .MoveNext
          EndWith   
       EndDo   
       oRecordset.MoveNext
    EndDo
    

    With the basics of hierarchical recordsets out of the way, we can turn our attention to a more complicated, real-life example. The following example adds several dimensions to the recordset.

    First, the Order Details table is appended to the Orders child recordset. In this case, a new field that will in turn point to the OrderDetails recordset, is added to the Orders recordset. The Products table is then appended to the OrderDetails recordset providing three levels of nesting. Appended to the Products recordset are two tables, Categories and Suppliers. Traversing up the hierarchy to the Orders recordset appends the Employees table.

    This list illustrates the hierarchy and shows all the tables involved as well as the nesting scheme. When creating reports, it is quite possible that you will need all of these tables. The ability to relate tables in this fashion and the ability to display the data in a user interface or a report have always been true powers of Visual FoxPro. Before ADO, attempting all this work outside Visual FoxPro was extremely difficult, sometimes bordering on the impossible.

    Customers
    
    Orders
    
    OrderDetails
       Products
          Categories
          Suppliers
    Employees
       EmployeeTerritories
          Territories
             Region
    Shippers
    
    

    The following is the Shape syntax to create the hierarchical recordset:

    SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND (( SHAPE 
    {SELECT * FROM "dbo"."Orders"} AS Orders APPEND (( SHAPE {SELECT * FROM 
    "dbo"."Order Details"} AS OrderDetails APPEND (( SHAPE {SELECT * FROM 
    "dbo"."Products"} AS Products APPEND ({SELECT * FROM "dbo"."Categories"} 
    AS Categories RELATE 'CategoryID' TO 'CategoryID') AS Categories,({SELECT 
    * FROM "dbo"."Suppliers"} AS Suppliers RELATE 'SupplierID' TO 
    'SupplierID') AS Suppliers) AS Products RELATE 'ProductID' TO 
    'ProductID') AS Products) AS OrderDetails RELATE 'OrderID' TO 'OrderID') 
    AS OrderDetails,(( SHAPE {SELECT * FROM "dbo"."Employees"} AS Employees 
    APPEND (( SHAPE {SELECT * FROM "dbo"."EmployeeTerritories"} AS 
    EmployeeTerritories APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"} 
    AS Territories APPEND ({SELECT * FROM "dbo"."Region"} AS Region RELATE 
    'RegionID' TO 'RegionID') AS Region) AS Territories RELATE 'TerritoryID' 
    TO 'TerritoryID') AS Territories) AS EmployeeTerritories RELATE 
    'EmployeeID' TO 'EmployeeID') AS EmployeeTerritories) AS Employees RELATE 
    'EmployeeID' TO 'EmployeeID') AS Employees,({SELECT * FROM 
    "dbo"."Shippers"} AS Shippers RELATE 'ShipVia' TO 'ShipperID') AS 
    Shippers) AS Orders RELATE 'CustomerID' TO 'CustomerID') AS Orders
    

    This is just about as complicated as it gets. Nobody in their right mind would want to hammer this code out manually. Fortunately, there is a visual way to build this code. The DataEnvironment designer that ships with Visual Basic allows you to visually design ADO connections, recordsets, and hierarchical recordsets. The following illustrates how this hierarchical recordset appears in the designer:

    The extensive Shape syntax can be copied and pasted into Visual FoxPro, or any other environment that can host ADO. For complete details on how to use the DataEnvironment designer, consult the Visual Basic documentation on the MSDN CDs that ship with Visual Studio.

    The following Visual FoxPro code traverses the hierarchical recordset and displays the data:

    #Include adovfp.h
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"}  AS Customers APPEND'
    cShpStr = cShpStr + '(( SHAPE {SELECT * FROM "dbo"."Orders"}  AS Orders '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Order 
    Details"}  AS OrderDetails '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Products"}  
    AS Products '
    cShpStr = cShpStr  + 'APPEND ({SELECT * FROM "dbo"."Categories"}  AS 
    Categories '
    cShpStr = cShpStr  + 'RELATE "CategoryID" TO "CategoryID") AS 
    Categories,'
    cShpStr = cShpStr  + '({SELECT * FROM "dbo"."Suppliers"}  AS Suppliers '
    cShpStr = cShpStr  + 'RELATE "SupplierID" TO "SupplierID") AS Suppliers) 
    AS Products '
    cShpStr = cShpStr  + 'RELATE "ProductID" TO "ProductID") AS Products) AS 
    OrderDetails '
    cShpStr = cShpStr  + 'RELATE "OrderID" TO "OrderID") AS OrderDetails,'
    cShpStr = cShpStr  + '(( SHAPE {SELECT * FROM "dbo"."Employees"}  AS 
    Employees '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM 
    "dbo"."EmployeeTerritories"}  AS EmployeeTerritories '
    cShpStr = cShpStr  + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"}  AS Territories '
    cShpStr = cShpStr  + 'APPEND ({SELECT * FROM "dbo"."Region"}  AS Region '
    cShpStr = cShpStr  + 'RELATE "RegionID" TO "RegionID") AS Region) AS 
    Territories '
    cShpStr = cShpStr  + 'RELATE "TerritoryID" TO "TerritoryID") AS 
    Territories) AS EmployeeTerritories '
    cShpStr = cShpStr  + 'RELATE "EmployeeID" TO "EmployeeID") AS 
    EmployeeTerritories) AS Employees '
    cShpStr = cShpStr  + 'RELATE "EmployeeID" TO "EmployeeID") AS Employees,'
    cShpStr = cShpStr  + '({SELECT * FROM "dbo"."Shippers"}  AS Shippers '
    cShpStr = cShpStr  + 'RELATE "ShipVia" TO "ShipperID") AS Shippers) AS 
    Orders '
    cShpStr = cShpStr  + 'RELATE "CustomerID" TO "CustomerID") AS Orders '
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security 
    Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    Do While !oRecordset.Eof
       With oRecordset
          ?.Fields("CustomerID").Value,.Fields("CompanyName").Value
       EndWith
       oOrders =  oRecordset.Fields("orders").Value
       Do While !oOrders.Eof
          oShippers = oOrders.Fields("shippers").Value
          oEmployee = oOrders.Fields("employees").Value
          oEmployeeTerritories = 
    oEmployee.Fields("employeeterritories").Value
          oTerritories = oEmployeeTerritories.Fields("territories").Value
          oRegion = oTerritories.Fields("region").Value
          ?"Order ID:  ",oOrders.Fields("orderid").Value,;
          "Order Date:  ",oOrders.Fields("orderdate").Value
          oOrderDetails = oOrders.Fields("orderdetails").Value
          ?"Territory:  ", 
    oTerritories.Fields("territorydescription").Value,;
          "Region:  ",oRegion.Fields("RegionDescription").Value
          ?"Shipper: ",oShippers.Fields("companyname").Value
          oEmployee = oOrders.Fields("employees").Value
          With oEmployee
             ?"Employee: ",.Fields("employeeid").Value,;
             .Fields("firstname").Value + " " + .Fields("lastname").Value
          EndWith   
          ?"Order Details:  "
          Do While !oOrderDetails.Eof
             oProducts = oOrderDetails.Fields("Products").Value
             oCategories = oProducts.Fields("categories").Value
             oSuppliers = oProducts.Fields("suppliers").Value
             ?Chr(9),;
              oProducts.Fields("productname").Value,;
              oSuppliers.Fields("companyname").Value,;
              oCategories.Fields("categoryname").Value,;
              oOrderDetails.Fields("Quantity").Value,;
              oOrderDetails.Fields("UnitPrice").Value
              oOrderDetails.MoveNext
          EndDo
          oOrders.MoveNext
       EndDo   
       oRecordset.MoveNext
    EndDo
    

    The output appears as follows:

    Because a hierarchy exists, the ability to create drill-down interfaces becomes a fairly simple task. The preceding Visual FoxPro code illustrates how to traverse the hierarchy.

    Perhaps you want to use Microsoft Word or Excel as a reporting tool. With a combination of Visual FoxPro COM servers, ADO, and Automation, the process becomes manageable. The first and third parts of the solution have been around. However, only now that a set of COM objects exists to handle and work with data as Visual FoxPro does natively can the solution become a reality.

    Hierarchical recordsets and recursive relationships

    One of the nice features of SQL Server, and of most other server back ends is provision for recursive relations. The following is the SQL Server 7.0 database diagram for the Northwind database:

    In the Northwind database, the Employees table employs recursion to support a manager/staff relationship. Both managers and staff are employees. In some cases, it happens that some employees report to other employees. In Visual FoxPro, you can create the same sort of relation by opening a table twice using two different aliases. In ADO, the task is totally supported and is quite easy to implement. The following is the Shape syntax:

    SHAPE {SELECT * FROM "dbo"."Employees"}  AS Managers APPEND ({SELECT * 
    FROM "dbo"."Employees"}  AS Staff RELATE 'EmployeeID' TO 'ReportsTo') AS 
    Staff
    

    The following Visual FoxPro code displays a list of managers and the staff that reports to each manager:

    #Include adovfp.h
    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    cShpStr = 'SHAPE {SELECT * FROM "dbo"."Employees"}  AS Managers '
    cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Employees"} AS Staff '
    cShpStr = cShpStr + 'RELATE "EmployeeID" TO "ReportsTo") AS Staff '
    With oConnection
       .Provider = "MSDataShape"
       .ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security 
    Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = cShpStr
       .CursorType = adOpenStatic
       .LockType = adLockBatchOptimistic
       .CursorLocation = adUseClient
       .Open
    EndWith
    Do While !oRecordset.Eof
       oStaff = oRecordset.Fields("staff").Value
       If oStaff.Recordcount > 0
          With oRecordset
             ?.Fields("firstname").Value + " " + ;
             .Fields("lastname").Value ,;
             .Fields("Title").Value
             Do While !oStaff.Eof
                With oStaff
                   ?Chr(9),;
                   .Fields("firstname").Value + " " + ;
                   .Fields("lastname").Value ,;
                   .Fields("Title").Value
                EndWith
                oStaff.MoveNext
             EndDo
          EndWith
       Endif
       oRecordset.MoveNext
    EndDo
    

    The output appears as follows:

    Finally, note that hierarchical recordsets are updateable. The following code expands the previous example to illustrate how to make a simple update:

    Do While !oRecordset.Eof
       oStaff = oRecordset.Fields("staff").Value
       If oStaff.Recordcount > 0
          With oRecordset
             Do While !oStaff.Eof
                With oStaff
                   .Fields("firstname").Value = ;
                      Upper(.Fields("firstname").Value)
                   .Fields("lastname").Value = ;
                      Upper(.Fields("lastname").Value)
                   .Fields("Title").Value = ;
                      Upper(.Fields("Title").Value)
                EndWith
                oStaff.MoveNext
             EndDo
             */ Write changes to Staff recordset
             oStaff.UpdateBatch
          EndWith
       Endif
       oRecordset.MoveNext
    EndDo
    

    The ability to view related records, coupled with the ability to make updates, places the ADO hierarchical recordset capability on par with similar capabilities in Visual FoxPro.

    Multiple recordsets

    Use of hierarchical recordsets represents only one method for returning data from multiple recordsets in one object. For starters, building hierarchical recordsets is not the most straightforward of propositions. In many cases, a simpler alternative may be all that is required.

    Consider the case where you need a specific customer record and the orders for that customer. Yes, you could use a hierarchical recordset. But, there is a simpler way: run two SQL statements.

    Some OLE DB providers can process multiple SQL Statements. The OLE DB Provider for SQL Server has this capability. Attempting to do this with Visual FoxPro tables via the OLE DB Provider for ODBC will not work.

    When using this technique, you have two choices on where the logic exists to perform the task. One choice is to build the SQL on the client and pass it to the server through a Command object. The other choice is to invoke a stored procedure on the database server through a Command object. I’ll illustrate both techniques. The Command object will be discussed in detail later in this paper.

    To illustrate the stored procedure method, the following stored procedure must be created on the SQL Server Northwind database:

    CREATE  PROCEDURE CustomerAndOrders @CustomerID nchar(5)
    AS
    Select * From Customers Where Customers.CustomerID = @CustomerID
    Select * From Orders Where Orders.CustomerID = @CustomerID 
    

    With the stored procedure created, the following code will create the recordset:

    #Include adovfp.h
    oConnection = CreateObject("adodb.connection")
    oCommand = CreateObject("adodb.command")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = ;
          "Persist Security Info=False;User ID=sa;Initial
            Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oCommand
       .CommandText = "CustomerAndOrders"
       .ActiveConnection = oConnection 
       .CommandType = adCmdStoredProc 
    EndWith
    oCommand.Parameters("@CustomerID").Value = "ALFKI"
    oRecordset = oCommand.Execute
    Do While !oRecordset.Eof
       ?oRecordset.Fields(1).Value
       oRecordset.MoveNext
    EndDo
    oRecordset = oRecordset.NextRecordset
    Do While !oRecordset.Eof
       ?oRecordset.Fields(0).Value
       oRecordset.MoveNext
    EndDo
    

    Like any recordset, the recordset just produced can be navigated. Once the first set of records from the Customers table have been navigated, the NextRecordset method is invoked. This causes the recordset produced by the second SQL statement to become available. Thus, the next set of commands loops through the records from the Orders table. This technique is ideal in those situations where you may need to populate Combo or ListBox controls.

    The previous example references a collection that has not been discussed yet, the Parameters collection. The Parameters collection and the individual Parameter objects that it contains serve several purposes. One purpose is to provide the capacity to create parameterized queries. Another purpose is to provide the ability to send arguments to, and return data from, a stored procedure. For more information on the Parameters collection, see the Command Object section of this paper.

    Alternatively, you can produce the SQL on the client if you wish. The following code illustrates the difference:

    With oCommand
       .CommandText = "Select * From Customers Where CustomerID =
          'ALFKI'" + Chr(13) + "Select * From Orders Where CustomerID =
          'ALFKI'"
       .ActiveConnection = oConnection 
       .CommandType = adCmdText 
    EndWith
    oRecordset = oCommand.Execute
    

    The same result is achieved. The difference lies in how the result is achieved.

    Which approach is better?

    It depends on what your requirements are. The first option, which uses stored procedures, is more secure; the code is set and you can assign permissions with regard to who can execute the stored procedure. The second option provides more flexibility, but less security.

    Fabricated recordsets

    Up to this point, recordset objects have been presented in the context of origination from an ADO connection. In many cases, you may want to create an ADO recordset with data that does not come from a data source, just as you may in some cases use the Create Cursor command in Visual FoxPro. For example, you may have an application that works with a small amount of data, such as an array or Visual FoxPro cursor. Perhaps you need to dynamically build a table structure. Whatever the reason, the ability to create ADO recordsets from scratch is powerful.

    To illustrate this capability, consider the need to fetch a list of files from a specified directory. In Visual FoxPro, a handy function, ADIR( ), performs this sort of task. However, what if you need to pass the data to another application? Or, perhaps you need to persist the list to a file on disk. While Visual FoxPro arrays are powerful, ADO recordsets provide a compelling alternative. The following code fetches a list of files from a specified directory, fabricates a recordset, and copies the values from the array into the newly created recordset:

    */GetFiles.prg
    #INCLUDE "adovfp.h"
    Local Array aFiles[1]
    Local nFiles,nField,nFile,oRS
    nFiles = Adir(aFiles,Getdir( )+"*.*")
    oRS=Createobject("adodb.recordset")
    With oRS
    .CursorLocation=ADUSECLIENT
    .LockType=ADLOCKOPTIMISTIC
    */ Adding new fields is a matter of appending
    */ new field objects to the Fields Collection. 
    .Fields.Append("File",ADCHAR,20)
    .Fields.Append("Size",ADDOUBLE,10)
    .Fields.Append("DateTime",ADDBTIME,8)
    .Fields.Append("Attributes",ADCHAR,10)
    .Open
    EndWith
    For nFile = 1 To nFiles
       */ Add a new record. This automatically makes
       */ the new record the current record - just
       */ like VFP.
       oRS.AddNew
       With ors
          .Fields("File").Value = aFiles[nFile,1]
          .Fields("Size").Value = aFiles[nFile,2]
          .Fields("DateTime").Value = ;
            Ctot(Dtoc(aFiles[nFile,3]) + " " + aFiles[nFile,4])
          .Fields("Attributes").Value = aFiles[nFile,5]
       EndWith
    Next nItem
    Return oRS
    

    With the new recordset created and populated, it can be navigated like any other recordset:

    oFiles = GetFiles ( )
    Do While !oFiles.Eof
       ?oFiles.Fields("File").Value
       oFiles.movenext
    EndDo
    

    ADO recordsets instead of arrays

    Referring to the previous example, let’s say that the list needs to be sorted by file size, descending. Arrays in Visual FoxPro can be sorted, when all columns in the array are of the same data type. In this case, there are three data types: Character, Numeric, and DateTime. With a client-side ADO recordset, the process becomes simple. The following code does the trick:

    oRS.Sort = "Size Desc"
    

    Sorts are not limited to just one column. Perhaps you need to sort by size, descending, and then by file, ascending:

    oRS.Sort = "Size Desc,File"
    

    And, when it comes to sorting, such properties as Bookmark and AbsolutePosition that have already been demonstrated are available here as well.

    Perhaps you need to find a specific value. The ASCAN( ) function in Visual FoxPro enables you to do this. However, it does not allow you to specify a particular column to search. Rather, once the first occurrence of a specified value is found, regardless of the column, the search is stopped. With ADO recordsets, more granular control is provided. The following code checks to see if a file called VFP6.EXE is in the recordset:

    oRS.Find("File Like 'VFP6.EXE'")
    If !oRS.Eof
       */ Found it
    Else
       */ Not found
    Endif
    

    Finally, you may wish to filter the list based on the file size being greater than a specified value:

    oRS.Filter = "size > 50000"
    

    When evaluating the tools at your disposal for local data handling, be sure to consider fabricated ADO recordsets. Also, if you find yourself running into obstacles with Visual FoxPro arrays, fabricated ADO recordsets may provide a sound alternative.

    Command Object

    ProgID: ADODB.Command

    The purpose of the Command object is just as the its name implies, to run commands. For example, you may need to run a SQL update against a SQL Server table. To illustrate, the following code applies a 10 percent increase in the UnitPrice field in the Products table of the SQL Server Northwind database:

    oCommand = CreateObject("adodb.command")
    With oCommand
       .ActiveConnection = oConnection
       .CommandText = "Update Products Set unitprice = unitprice * 1.1"
       .Execute
    EndWith
    

    The ActiveConnection property

    To review, both the Command object and Recordset object have the ActiveConnection property. A Command object needs to know what data source it is to execute commands against. A Recordset object needs to know what data source contains the data it is to retrieve. The way you accomplish this is by setting the ActiveConnection property.

    The ActiveConnection property presents a great opportunity to talk about the flexible nature of the ADO object model. The ADO object model is very flat, in that you do not have to create a series of objects in order to gain access to other objects. For example, the following is one way to create and open both a Connection and a Recordset object:

    oConnection = CreateObject(""adodb.connection"")
    oRecordset = CreateObject(""adodb.recordset"")
    With oConnection
       .Provider = ""SQLOLEDB.1""
       .ConnectionString = ""Persist Security Info=False;User 
        ID=sa;Initial Catalog=Nothwind;Data Source=JVP""
       .Open
    EndWith
    With oRecordset
       .ActiveConnection = oConnection
       .Source = ""Products""
       .Open
    EndWith
    

    Here is another way to create the two objects:

    oRecordset = CreateObject(""adodb.recordset"")
    With oRecordset
       .ActiveConnection = ""Provider=SQLOLEDB.1;Persist Security 
        Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP""
       .Source = ""Products""
       .Open
    EndWith
    

    Now, you can reference the Connection object because it has been implicitly created from the passed connection string:

    ?oRecordset.ActiveConnection.ConnectionString
    

    The same is true for the Command object. While a Command object was not explicitly created, a Command object was in fact created and actually did the work of creating the recordset. Using the recordset just created, the following command will yield “Products” as the CommandText:

    ?oRecordset.ActiveCommand.CommandText
    

    Which method should you use?

    It is really a matter of preference. The latter method, which uses only the RecordSet object, is somewhat overloaded. It carries the same overhead as the former method because you must still create a Connection object. The former method is probably a better way to go as it makes for more readable code.

    Parameters collection

    The Parameters collection works with the Command object. The primary use of the Parameters Collection is to both pass arguments to, and accept return values from stored procedures. To illustrate, consider the CustOrderHist stored procedure in the SQL Server Northwind database:

    CREATE PROCEDURE CustOrderHist @CustomerID nchar(5)
    AS
    SELECT ProductName, Total=SUM(Quantity)
    FROM Products P, [Order Details] OD, Orders O, Customers C
    WHERE C.CustomerID = @CustomerID
    AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND 
    OD.ProductID = P.ProductID
    GROUP BY ProductName
    

    To illustrate how the Parameters collection is used in conjunction with the Command object, consider the following comprehensive example:

    First, you need to establish a valid connection:

    oConnection = CreateObject("adodb.connection")
    

    Next, the connection needs to be opened.

    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    

    With a valid, open connection, a Command object can be prepared:

    With oCommand
       .ActiveConnection = oConnection 
       .CommandText = "CustOrderHist"
       .CommandType = adCmdStoredProc && adCmdStoredProc = 4
    EndWith
    

    At this point, information can be obtained from the Parameters collection:

    For Each Parameter in oCommand.Parameters
       ?Parameter.Name,Parameter.Size,Parameter.Type
    Next Parameter
    

    The first Parameter object is reserved for the value that the stored procedure may return. Regardless of whether the stored procedure explicitly returns a value, this Parameter object will be created. Examining the CustOrderHist stored procedure, note that a single argument, a customer ID, is accepted.

    With a Command object and Parameter object in place, the real work can begin. To get things rolling, a value needs to be assigned to the Parameter object that will in turn be passed to the stored procedure. In this case, a SQL statement is executed that totals the quantity, by product, that a specified customer has purchased. The following code provides a customer ID and executes the stored procedure:

    oCommand.Parameters("@CustomerID").Value = "ALFKI"
    oRecordset = oCommand.Execute
    

    Yet another way to produce a Recordset object is through the execution of a stored procedure. The resulting Recordset object contains two fields that correspond to the select statement in the CustOrderHist stored procedure. Need a different history? Just update the Value property of the Parameter object and invoke the Execute method of the Command object.

    The Parameters collection also comes into play in the area of parameterized queries. Consider the following SQL Statement:

    Select * ;
       From Customer ;
       Where country = ? And max_order_amt > ?
    

    As with views, either local or remote, in Visual FoxPro, so too can queries be parameterized in ADO. In ADO, the question mark acts as a placeholder for parameters. The following example illustrates how to put this all together.

    First, a connection and a Command object need to be created:

    oConnection = CreateObject("adodb.connection")
    oCommand = CreateObject("adodb.command")
    

    Next, the connection needs to be established:

    oConnection.Open("northwind","sa","")
    

    For illustration purposes, the OLE DB Provider for ODBC is used. The native OLE DB Provider for SQL Server could have been used as well.

    Next, the Command object needs to be prepared:

    With oCommand
       .ActiveConnection = oConnection
       .CommandText = "Select * From Customer Where country = ? 
    EndWith
    

    With the Command object ready to go, a parameter object needs to be created:

    oCountryParameter = ;
     oCommand.CreateParameter("country",adChar,adParamInput,1," "))
    

    The arguments for the CreateParameter method are as follows:

    • Name—The name of the parameter.
    • Type—The data type of the parameter. A list of valid values is contained in DataTypeEnum.
    • Direction—The direction of the parameter. Parameters sent to a command are input parameters. Arguments passed back from a command are output parameters. A list of valid values is contained in ParameterDirectionEnum.
    • Size—The length of the parameter.
    • Value—The initial value of the parameter.

    Alternatively, the parameter could have been created like this:

    OCountryParameter = CreateObject("adodb.parameter")
    With oCountryParameter
       .Name = "Country"
       .Type = adChar
       .Direction = adParamInput
       .Size = 1
       .Value = " "
    EndWith
    

    Once the parameter has been created, it needs to be appended into the Parameters collection of the Command object:

    oCommand.Parameters.Append(oCountryParameter)
    

    With the parameter in place, the value of the parameter can be set. In this case, the parameter will be set so that any country that begins with the letter U will be returned into a Recordset object:

    With oCountryParameter
       .Size = 2
       .Value = "U%"
    EndWith
    

    Now, a Recordset object can be created:

    oRecordset = oCommand.Execute
    

    A useful feature of specifying parameters is that this enforces characteristics such as size, data type, and so on. For example, the preceding parameter was defined as a character. If a value based on a different data type was assigned to the Value property of the Parameter object, an error would result. The same is true if the assigned value is greater in length than what has been specified by the Size property.

    Finally, if a list of customers in Mexico were required, the following code would complete the task:

    With oCommand
       .Parameters("country").Size = Len("Mexico")
       .Parameters("country").Value = "Mexico"
       oRecordSet = .Execute 
    EndWith
    

    Properties Collection

    Recall the earlier assertion that, by itself, ADO is incapable of doing anything? ADO in fact just provides an interface. OLE DB providers give ADO the ability to do anything. So then, what distinguishes one OLE DB provider from another? More specifically, how can you determine what an OLE DB provider can and cannot do, or what attributes it does or does not possess? Depending on the OLE DB provider you use, or the type of recordset you use (client or server), what is supported will likely differ.

    The Properties collection applies to the ConnectionRecordset, and Field objects. The Command object also has a Properties collection, which is identical to the Recordset object Properties collection.

    Multiple result sets provide a good example of varying OLE DB provider support. To determine if multiple result sets can be obtained, you can refer to the “Multiple Results” properties:

    If oConnection.Properties("Multiple Results").Value = 1
       */ Supports multiple result sets
    EndIf
    

    While the OLE DB providers for SQL Server and ODBC both support multiple results, the OLE DB provider for Jet does not. To illustrate, the following is valid syntax for SQL Server:

    oRecordset.Source="SELECT * FROM customers;"+"SELECT * FROM orders"
    oRecordset.Open
    ?oRecordSet.Fields.Count && number of fields in customers table
    oRecordset = oRecordset.NextRecordSet
    ?oRecordSet.Fields.Count && number of fields in orders table
    

    In this case, the OLE DB Provider for SQL Server can return multiple recordsets. If you attempt the same thing with the OLE DB Provider for ODBC, which you need to use when accessing Visual FoxPro data, you will receive an error message stating that the requested action is not supported by the OLE DB provider.

    Another example involves the way in which the Properties collection deals with the location of a Recordset object. Recordsets can either exist locally as client-side recordsets or they can exist remotely as server-side recordsets. Client-side recordsets, as will be discussed shortly, have several capabilities that server-side recordsets do not have. One of these abilities is to create indexes. The following code creates a client-side recordset:

    oRecordset = CreateObject("adodb.recordset")
    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "SQLOLEDB.1"
       .ConnectionString = "Persist Security Info=False;User 
        ID=sa;Initial Catalog=Northwind;Data Source=JVP"
       .Open
    EndWith
    With oRecordset
       .Cursorlocation = adUseClient && adUseClient = 3
       .ActiveConnection = oConnection
       .Source = "Products"
       .Open 
    EndWith
    

    Now, lets create an index on the ProductName field using the following code:

    oRecordSet.Fields("productname").Properties("optimize").Value = .T.
    

    In the absence of a declaration of where a Recordset object should reside, the Recordset object, by default, resides on the server. Attempting to reference the Optimize property results in an error stating that the specified property could not be found in the collection.

    While the ADO interface is constant, depending on the provider you use, the capabilities may be very different. Be sure to consult your provider’s documentation.

    Remote Data Services

    One of the most powerful data access capabilities introduced by Microsoft is Remote Data Services (RDS). Although a separate set of objects exists for RDS, RDS is really just another component for use with ADO. There are two ways you can implement RDS.

    • Use the same ADO objects described in this paper
    • Use the RDS data control

    Let’s discuss the RDS data control option first, since it represents some uncharted territory.

    The RDS Data Control

    The following code creates an instance of the RDS data control:

    oRDSDataControl = Createobject("rds.datacontrol")
    

    Once the data control is created, only three properties need to be populated: ServerConnect, and SQL.

    With oRDSDataControl
       .Server = "http://jvp"
       .Connect = ;
        "Remote Provider=SQLOLEDB.1;database=northwind;User ID=sa;"
       .Sql = "Customers"
    EndWith
    

    Because we’re using the SQL Server OLE DB Provider, the SQL property can consist of just the table name. The following code retrieves the same recordset, but does so with the OLE DB provider for ODBC:

    With oRDSDataControl
       .Server = "http://jvp"
       .Connect = "dsn=northwind;uid=sa;pwd=;"
       .Sql = "Customers"
    EndWith
    

    Whenever possible, you should use a native OLE DB provider rather than the OLE DB provider for ODBC.

    With the RDS data control properties set, you can create a recordset. Invoke the Refresh method to accomplish this, as in the following code:

    oRDSDataControl.Refresh
    oRecordset = oRDSDataControl.Recordset
    

    From this point on, you can work with the recordset the same way you work with any other ADO client-side recordset:

    Do While !oRecordset.Eof
       orecordset.Fields(1).value = ;
          Proper(orecordset.Fields(1).value)
       oRecordset.Movenext
    EndDo
    oRecordset.Updatebatch
    

    Alternatively, you can replace the last line of code with a call to the SubmitChanges method of the RDS data control:

    oRDSDataControl.SubmitChanges
    

    Implementing RDS Through the ADO Interface

    You can invoke RDS by using the same ADO Connection object discussed above. As with hierarchical recordsets, the first step involves the selection of an OLE DB provider. In this case, the MSRemote provider is required. The following code sets up the Connection object:

    oConnection = CreateObject("adodb.connection")
    With oConnection
       .Provider = "MS Remote.1"
    
       .ConnectionString = "Remote Server=http://jvp;Remote 
             Provider=SQLOLEDB.1;database=northwind;User ID=sa;Pwd=;"
       .Open
    EndWith
    

    The ADO ConnectionString property supports only four arguments. The first two, Provider and File Name, have already been discussed. The third and fourth, Remote Provider and Remote Server, are used by the RDS in the example above. The Remote Provider is the same OLE DB provider used when you create local connections. The additional parameters that specify the database, user ID, and password are used by the OLE DB Provider for SQL Server that in turn is located on the remote server. The following code connects the Recordset object and Connection object and with one difference, is basically the same as the previous examples in this paper:

    With oRecordset
       .ActiveConnection = oConnection
       .Source = "Customers"
       .LockType = adLockBatchOptimistic
       .Open
    EndWith
    

    The only difference is that properties such as CursorLocation and CursorType are omitted since all recordsets created through RDS must exist on the client. Additionally, all client-side recordsets are static types. If you like, you can still specify the properties explicitly. Any incompatible properties will be coerced to a valid value. For example, if you specify the CursorType to be a ForwardOnly cursor and you specify the recordset exists on the client, when the Open method is fired, ADO forces the cursor type to be static. The same is true if you specify the CursorLocation to be on the server and you use the MSDataShape provider. Since all hierarchical pecordsets must exist on the client, the CursorLocation is coerced to the proper value.

    Summary

    The goal of this paper has been to provide you with a fairly comprehensive overview of both ADO and RDS from the perspective of Visual FoxPro applications. Note that ADO is not a replacement for the Visual FoxPro Cursor Engine. Rather, regard it as another tool at your disposal. Both Visual FoxPro cursors and ADO recordsets have their relative strengths and weaknesses.

    ADO is ideal in situations where your application is component based, or in situations where you need to pass data to other applications such as Excel in automation operations. Fabricated ADO recordsets can provide an interesting alternative to arrays when more robust data handling requirements are necessary.

    For most local data handling operations however, Visual FoxPro cursors will usually provide better results.

    John V. Petersen, MBA, is president of Main Line Software, Inc., based in Philadelphia, Pennsylvania. John’s firm specializes in custom software development and database design. He is a Microsoft Most Valuable Professional and has spoken at many developer events, including Visual FoxPro Developers Conference, FoxTeach, the Visual FoxExpress Developer’s Conference, DevDays, and TechEd. In addition, John has written numerous articles for FoxTalk and FoxPro Advisor. John is co-author of Visual FoxPro 6 Enterprise Development and Hands-on Visual Basic 6—Web Development, both from Prima Publishing. John’s latest project is the ADO Developer’s Handbook, from Sybex Publishing, due September 1999.

    E-mail: jpetersen@mainlinesoftware.com

    Books

    1. “Programming FoxPro 2.5” by Lisa Slater and Steven D. Arnott
      • A foundational book for beginners and intermediate users of FoxPro 2.5. Covers database management and programming techniques.
    2. “The Revolutionary Guide to Visual FoxPro 3.0” by Kevin McNeish and Marilyn McLain
      • This book explores Visual FoxPro 3.0, detailing object-oriented programming features and database development.
    3. “Visual FoxPro 6.0 Programmer’s Guide” by Tamar E. Granor, Ted Roche, Doug Hennig, and Dave Fulton
      • A comprehensive guide to using Visual FoxPro 6.0 for professional database application development.
    4. “Advanced Object-Oriented Programming with Visual FoxPro 6.0” by Markus Egger
      • Focuses on object-oriented techniques, design patterns, and advanced development strategies.
    5. “Hacker’s Guide to Visual FoxPro 7.0” by Tamar E. Granor and Ted Roche
      • A deep dive into advanced features and lesser-documented aspects of Visual FoxPro 7.0.
    6. “Microsoft Visual FoxPro: Language Reference”
      • Official Microsoft documentation providing a detailed reference to the Visual FoxPro language, commands, and functions.
    7. “Fundamentals of FoxPro 2.x Programming” by George Goley
      • Covers foundational programming techniques and database management in early FoxPro versions.

    Online Resources

    1. Microsoft Visual FoxPro Developer Center
    2. Foxite – Visual FoxPro Community Resource
      • https://www.foxite.com
        A community forum where FoxPro and Visual FoxPro developers share tips, tricks, and solutions.
    3. UtterAccess Visual FoxPro Forum
    4. The Universal Thread

    Journal Articles

    1. “Visual FoxPro 9.0 and Beyond”
      • Published in FoxTalk, this article discusses updates and features in the final version of Visual FoxPro, as well as transitioning to other platforms.
    2. “Bringing Old FoxPro Applications into the Modern Era”
      • Journal of Database Development, 2010, discusses strategies for updating legacy FoxPro applications.

    Additional Resources

    1. CodePlex Archive for Visual FoxPro Projects
    2. GitHub: VFPX (Visual FoxPro Extensions)
      • https://github.com/VFPX
        A collaborative project to extend the life of Visual FoxPro by providing tools, add-ons, and updates.

    Books

    1. “Visual FoxPro 6.0 Programmer’s Guide”
      • Authors: Tamar E. Granor, Ted Roche, Doug Hennig, and Dave Fulton
      • Publisher: Que
      • Description: Comprehensive coverage of Visual FoxPro 6.0 features, including database development, debugging, and deployment techniques.
    2. “Advanced Object-Oriented Programming with Visual FoxPro 6.0”
      • Author: Markus Egger
      • Publisher: Hentzenwerke
      • Description: An in-depth guide to using object-oriented programming techniques in Visual FoxPro 6.0, with practical examples.
    3. “Hacker’s Guide to Visual FoxPro 7.0”
      • Authors: Tamar E. Granor, Ted Roche
      • Publisher: Hentzenwerke
      • Description: Although for version 7.0, much of its content applies to version 6.0, covering advanced features and debugging tips.
    4. “What’s New in Visual FoxPro 8.0”
      • Author: Tamar E. Granor, Doug Hennig
      • Publisher: Hentzenwerke
      • Description: Provides insights into features that extend and build on those found in Visual FoxPro 6.0 and 7.0.
    5. “Special Edition Using Visual FoxPro 6”
      • Authors: Neil L. Clausen and Que Development Group
      • Publisher: Que
      • Description: Offers step-by-step guidance for Visual FoxPro 6.0 developers, including user interface design and SQL integration.
    6. “Visual FoxPro 9.0: Best Practices for Business Applications”
      • Author: Les Pinter
      • Publisher: Hentzenwerke
      • Description: A guide to building modern business applications using Visual FoxPro 9.0, emphasizing reporting, XML, and COM interoperability.
    7. “MegaFox: 1002 Things You Wanted to Know About Extending Visual FoxPro”
      • Authors: Marcia Akins, Andy Kramek, and Rick Schummer
      • Publisher: Hentzenwerke
      • Description: A deep dive into the extensibility features of Visual FoxPro 9.0, focusing on advanced development practices.

    Official Documentation

    1. “Microsoft Visual FoxPro 6.0 Documentation”
      • Publisher: Microsoft Corporation
      • Includes a comprehensive reference for commands, functions, and user interface components.
    2. “Microsoft Visual FoxPro 9.0 Service Pack 2 Documentation”
      • Publisher: Microsoft Corporation
      • Details enhancements, bug fixes, and updates introduced in the final version of Visual FoxPro.
    3. “Visual FoxPro 9.0: Language Reference”
      • Publisher: Microsoft Press
      • An essential guide for understanding Visual FoxPro’s programming language, covering every command, function, and system variable.

    Online Resources

    1. Microsoft Visual FoxPro Developer Center
    2. VFPX (Visual FoxPro eXtensions)
      • https://github.com/VFPX
      • An open-source repository hosting tools, extensions, and community-driven updates for Visual FoxPro.
    3. Foxite – Visual FoxPro Community Resource
    4. The Universal Thread – FoxPro Resource
    5. Visual FoxPro Wiki
      • https://fox.wikis.com
      • A collaborative resource with tutorials, best practices, and links to additional FoxPro content.

    Journal Articles

    1. “Visual FoxPro 9.0: Enhancements and Updates”
      • Published in FoxTalk, this article covers key features introduced in VFP 9.0, such as reporting enhancements and data handling.
    2. “Refactoring Legacy Applications in Visual FoxPro 6.0”
      • Published in Journal of Database Development, 2004, discussing strategies to modernize FoxPro 6.0 applications.

    By Amjad Izhar
    Contact: amjad.izhar@gmail.com
    https://amjadizhar.blog