Archive | 1995

OLE2 in Visual FoxPro 3.0

OLE Is No Bull

Visual FoxPro’s OLE capabilities move us closer to component-based application development.

By Ted Roche, Contributing Writer

[Originally published in FoxPro Advisor, June 1995. Don’t blame me for the headline (*groan*)!]

Most audiophiles aren’t interested in all-in-one music boxes that play CDs and cassettes, and tune the radio, all with one set of controls. Audiophiles prefer the greater reliability, interchangability, and configurability of a component stereo system.

Similarly, application developers today are discovering that a monolithic solution to business problems—a programming system that incorporates a word processor, serial communication protocols, and database manipulation into a single multi-megabyte .EXE file— doesn’t provide the flexibility to adapt their development system rapidly enough to meet today’s ever-changing business needs. What developers need is a component system. Visual FoxPro is leading us towards that vision of computing with the addition of OLE 2.0.

OLE 2.0 isn’t so much a single feature as a collection of related technologies. One group offers the ability to embed or link data into FoxPro general fields, a capability first introduced in FoxPro 2.5 (using OLE 1.0) and greatly enhanced in OLE 2.0-compliant Visual FoxPro. Visual Editing lets users manipulate OLE objects within the menu and toolbar interface of a VFP application. Another new feature is OLE Automation, the ability to programmatically command and control objects within other applications. Last, but by no means least, OLE Custom Controls give us the ability to plug entire pre-programmed components into our applications.

OLE becomes new again

Visual FoxPro retains backward compatibility while enhancing the OLE functionality of FoxPro 2.6, with continued support for general fields and the APPEND GENERAL, MODIFY GENERAL and @ … SAY oleobject VERB commands. APPEND GENERAL embeds or links data into a Visual FoxPro general field. The new DATA clause lets you pass data directly to an application when you create a new OLE object. This sample creates a tab-delimited data table in the proper format for Microsoft Graph’s datatable, and creates and displays a new graph object:

#DEFINE tab CHR(09)
#DEFINE crlf CHR(13)+CHR(10)
* Graph data columns are separated by tabs ;
* rows are separated by carriage return - line feed
* pairs
testdata = " " + tab + "X-Axis" + crlf + ;
"One" + tab + "10" + crlf + ;
"Two" + tab + "20" + crlf + ;
"Three" + tab + "30" + crlf
CREATE CURSOR SmplGrph (mygraph G)
APPEND BLANK
APPEND GENERAL mygraph CLASS "MSGraph" DATA testdata
MODIFY GENERAL mygraph

There have been no changes to the syntax of MODIFY GENERAL and @ … SAY oleoject VERB from 2.6. The MODIFY GENERAL command brings up an editing window displaying the OLE object. Double-clicking on the object starts the OLE server and allows changes to the data and appearance of the object. MODIFY GENERAL has a WINDOW option (as BROWSE does), which allows the new window to take on the characteristics of a pre-defined window. You can also specify that the MODIFY GENERAL window appear within another window, or on the main desktop with the IN WINDOW <windowsname> or IN SCREEN clauses. The @ … SAY command’s VERB option allows you to send one of a set of pre-defined messages to the OLE server application, such as Play or Edit. The Registration Database determines which verbs your particular application understands. You can examine it by issuing the RUN /N REGEDIT.EXE /V (for 16-bit Windows, or REGEDT32.EXE for Windows NT) command from within FoxPro.

Visual Editing

Visual Editing of a Graph

Double-clicking on a MODIFY GENERAL window displays another new feature—Visual Editing. In previous versions of FoxPro, the entire screen changed to that of the OLE server program, with a new title, menus, and toolbars. Studies show that users were jarred and disconcerted by this change. Visual Editing lets your users modify the OLE objects contained within your application, without the appearance of ever having left the application (figure 1).

Visual Editing allows addition of an object’s menus onto the current menu bar, and substitution of the object’s toolbars for your application’s toolbars. This gives the operator a much better sense of continuity and gives your application the appearance of far better integration.

To integrate Visual Editing with your application, you’ll need to modify your menu system to tell the OLE Application where it can place its menu pads. To allow for this, the DEFINE PAD command added the new keywords NEGOTIATE LEFT|MIDDLE|RIGHT, which determine where your menu pads should appear when OLE Visual Editing is going on. Omitting the keyword for any pad drops that pad during Visual Editing.

OLE Automation

With the addition of the CREATEOBJECT() and GETOBJECT() functions, Visual FoxPro exercises much finer control over OLE objects, sending commands to them directly, modifying their properties, or causing them to perform specific functions. This feature, referred to as OLE Automation for the Microsoft Office products, allows for much tighter integration of FoxPro with other applications.

CREATEOBJECT() creates an object from a defined class or an OLE object. When CREATEOBJECT() is executed, Visual FoxPro searches for a class definition with the specified name, first in the current program, then in the CLASSLIB, if one has been specified, and then up the program calling stack. If it fails to locate a class definition within FoxPro, it then searches the Windows Registration Database for an OLE Object with a matching name. If a match is found in the Registration Database, an OLE object is created and its associated server is started.

The neat thing about starting an application this way is that it’s not visible, and won’t be, unless you make it so. Once you’ve mastered the command syntax of the application you’re addressing, you can programmatically perform nearly any task the application is capable of, as if you had borrowed the mouse from the user’s hand and were clicking through the menus. In this simple example, I create an Excel 5.0 spreadsheet, populate a few of its data cells, perform a standard deviation calculation, capture the result, close the application, and release the object:

objXLApp=createobject("Excel.Application")
objXLApp.Workbooks.Add
objXLApp.cells(1,1).Value = "88"
objXLApp.cells(2,1).Value = "97"
objXLApp.cells(3,1).Value = "23"
objXLApp.cells(4,1).Value = "47"
objXLApp.cells(5,1).Value = "55"
objXLApp.cells(6,1).Value = "=STDEV(R[-5]C:R[-1]C)"
myresult = objXLApp.cells(6,1).Value
objXLApp.ActiveWorkbook.Close("False")
objXLApp.Quit
release objXLApp
wait window ltrim(str(myresult,5,2))

Unlike CREATEOBJECT(), which creates a new and empty OLE document, the GETOBJECT() function lets you open an existing document as an OLE Automation Object, then manipulate it. This example uses WordBasic to open an existing file, select all the text, copy it to the clipboard, and close the document.

objtalk2word =createobject("word.basic")
objtalk2word.fileopen("mydoc.doc")
objtalk2word.startofdocument
objtalk2word.endofdocument(1)
objtalk2word.editcopy
objtalk2word.fileclose
release objtalk2word
? _cliptext
? len(_CLIPTEXT)

OLE Custom Controls

Figure 2: Adding an OLE Custom Control to a Form

Figure 2: Adding an OLE Custom Control to a Form

In addition to linking to exposed objects within other applications, OLE 2.0 allows Visual FoxPro to use OLE Custom Controls (.OCXs) within. These custom controls are the 32-bit equivalent of Visual Basic’s .VBXs—custom tools built to work within VB. .VBXs took the VB world by storm, and a number of third-party developers created very innovative controls. However, .VBXs are specifically designed to work with Visual Basic, and can’t be easily extended to other languages or other platforms. To solve these limitations, OLE Custom Controls were defined. .OCXs were first introduced with the release of Access 2.0, and Microsoft has announced that .OCXs will be supported in all of their 32-bit development tools. .OCXs have the advantage of a larger market share than product-specific add-ons.

Embedding an OLE Custom Control in a form is simply a matter of clicking your way through a few dialogs. In a blank form, select the OLE Container Control. Draw the location on the form where you want the control to appear by clicking and dragging the mouse. A dialog appears; select the Custom Controls button, then the Custom Control you want to use. In the example shown in figures 2 and 3, I use the Outline control included with Visual FoxPro. After placing the control on the form, selecting it, and bringing up its properties, you’ll discover one of the great features of OLE Custom Controls—a Property sheet specific to the control (figure 3).

Outline Control Property Sheet

Outline Control Property Sheet

Check out the sample .OCXs included with Visual FoxPro (found in the SAMPLES\OLE subdirectory in the beta version used for this article) and keep an eye on these pages for reviews of other useful .OCXs.

Where are my objects?

There’s some excellent documentation available inexpensively on the OLE Automation Objects currently in the Microsoft Office products. The Microsoft Office Development Kit is a CD-ROM that includes :

  • Excel Object Model

    Figure 4: Office Developer’s Kit shows Excel Object Model

    Office OLE reference guides to all exposed objects in Excel, Access & Project (figure 4).

  • An Object Browser, listing properties and methods of all Office objects.
  • User and developer documentation for all Office products and for VB 3.0 Professional.
  • Microsoft Mail SDK.
  • Excel Development Kit.
  • .Word 6.0 Development Kit.
  • Microsoft Electronic Form Designer.

The ODK is bundled with newer packages of Visual Basic 3.0 Professional and is also available separately from most retail sources. The Microsoft stock number is 203-052-001, and the typical street price is less than US$100.

What will OLE do for me?

New OLE features simplify the integration of data and functionality from other applications. OLE-compliant applications let you integrate the expertise and debugged code of other developers to deliver more capable and richer-featured applications, while you concentrate on your customers’ needs.

Contributing Writer Ted Roche is a Microsoft Certified Professional, a 1994 recipient of Microsoft’s Most Valuable Professional award and principal in the FoxPro consulting firm, Computer Resource. Ted is co-author of FOXPRO ADVISOR Q&A column. His latest book project, with FoxPro Advisor Editor Tamar Granor, is the Hacker’s Guide to Visual FoxPro (Addison Wesley).

Copyright 1995, 2017 by Ted Roche under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) license, https://creativecommons.org/licenses/by-nc-sa/3.0/

Converting CardFiles to FoxPro tables

Import Non-.DBF Data

Explore one of the many uses of low-level file

functions: Convert Cardfile data to FoxPro tables.

By Ted Roche

[Originally published in FoxPro Advisor, January 1995]

What are LLFFs?

Low-level file functions (LLFFs) provide the ability to read and write files in foreign file formats within FoxPro. These files can be in any format—text files, binary data, such as bitmaps, or even .EXE files. (Caution: You’ll probably want to experiment on copies of important files, as there is no UnDo() function.) In this article, I discuss and demonstrate reading a Windows Cardfile into a FoxPro database. While LLFFs also provide limited ability to access other DOS devices (such as the serial port) directly, the focus of this article is on file manipulation.

Why use LLFFs?

Low-level file functions are useful in so many situations, your imagination may be all that limits them. LLFFs are used to read system files, such as CONFIG.SYS and AUTOEXEC.BAT, to ensure that required settings or commands are present. They are also used to read and correct corrupted FoxPro databases or indexes. As a security measure, they can be used to make your application’s .DBF files unreadable by others. Or, as in the example given here, they can be used to read in data files whose format is not supported by FoxPro’s IMPORT command.

How to use LLFFs

Using LLFFs in your program doesn’t require a different programming structure or approach than you have used for FoxPro’s native high-level functions. The main difference is that you need to be aware of any structure built into the files, whereas FoxPro knows how to work with .DBF, .CDX, and .FPT files. In fact, you could look at low-level file functions as providing access to files that consist of a sequence of one-byte records. Rather than USEing a database, you FOPEN() or FCREATE() a file. Instead of an alias or select area, you specify the file you’re using by referring to the file handle returned by the functions.

Rather than SCATTERing the contents of a .DBF’s fields, you FREAD() one or more bytes or FGETS() characters up to the next carriage return in the file. Conversely, you would FWRITE() one or more bytes or FPUTS() a character string, rather than REPLACEing or GATHERing memory variables to disk.

Because the file consists of unindexed one-byte records, there’s no equivalent to SEEK or LOCATE. Instead, you just FREAD() until you find the character you’re looking for. However, if you know the location in the file where you want to work, the FSEEK() function works similarly to the GO and SKIP

commands to reposition you within the file.

Finally, when you’re done with the file, you FCLOSE() it, just as you would USE a .DBF when done.

Error handling

LLFFs have their own error-handling capabilities that are separate from the usual ON ERROR trapping used by FoxPro. There are two levels of error handling. First, check for proper response when issuing a function. Then, investigate details of the error using the FERROR() function. For example, when FOPEN() is used, a non-negative number is returned if the file is opened successfully. If a negative number is returned, you know an error has occurred and you can proceed from there. Similarly, the reading, writing, and pointer repositioning functions return the number of bytes processed, so that an incomplete process can be detected and handled appropriately.

Putting LLFFs to work

The sample code in this article, demonstrates importing data from Windows’ Cardfile format. Cardfile is a Windows applet, a simple demonstration application that ships with Windows. It has basic indexing, search, and autodial capabilities and uses few resources, limiting itself to a 64K memory segment. For a simple database application or for machines severely limited in resources, Cardfile is a handy way of letting users view names, addresses, or other company information without incurring the heavy overhead of keeping a FoxPro session running.

The application, included on this issue’s COMPANION RESOURCE DISK and reproduced here, consists of three files. READCARD.PRG is the project’s main file and the driving program, ASC2Num.PRG is a UDF to convert binary-stored numbers to numerics, and LLError.PRG is a generic low-level error handler.

Bear in mind these are simplified routines designed to illustrate basic functionality, and additional safeguards may be needed for a commercial-grade application. The COMPANION RESOURCE DISK also contains a sample Cardfile containing the text of Lincoln’s Gettysburg address. (See the sidebar “Windows Cardfile Format” later in this article for a description of a Cardfile format.)

Main Program – READCARD.PRG

* READCARD.PRG - read in cardfile database
* Purpose: Read a Window CardFile,
*
Write a DBF
PRIVATE ALL LIKE L*
* Select Input CardFile and Output DBF *
****************************************
lcCardFile = GETFILE("CRD", ;
"Read which cardfile?","Read",0)
IF EMPTY(lcCardFile) OR NOT FILE(lcCardFile)
* Cancel or no file
RETURN
ENDIF
lcCardDBF = PUTFILE("Save As:", ;
STRTRAN(lcCardFile, "CRD", "DBF"), ;
"DBF")
IF EMPTY(lcCardDBF)
* an empty return -> cancel selected
RETURN
ENDIF
* Open the input Cardfile *
***************************
lnCardHandle = FOPEN(lcCardFile,0)
IF lnCardHandle = -1
DO llError
ENDIF
* Create the output table *
***************************
CREATE TABLE (lcCardDBF) (Topic C(50),Contents M)
* Read CardFile Header *
************************
IF FSEEK(lnCardHandle,0,0) # 0
DO llError
ENDIF
&& go to BOF
lcFileType = FREAD(lnCardHandle,3)
IF lcFileType # "MGC" AND lcFileType # "RRG"
WAIT WINDOW lcCardFile + ;
"is not a valid CardFile."
=FCLOSE(lnCardHandle)
RETURN
ENDIF
* Calculate the number of cards *
*********************************
* # cards, binary
lbCardCnt = FREAD(lnCardHandle,2)
DO llError
* # cards, decimal
lnCardCnt = ASC2Num(lbCardCnt)
* Loop through the cards *
**************************
FOR lnCardNum = 1 TO lnCardCnt
* read in cards
* Display a status window *
***************************
WAIT WINDOW NOWAIT "Reading card #" ;
+TRANSFORM(lnCardNum,"999")
* Position at the record *
**************************
lnPosition = 11 + 52 * (lnCardNum - 1)
IF lnPosition # FSEEK(lnCardHandle,lnPosition,0)
DO llError
ENDIF* The first five characters point to
* the location of the card's contents *
***************************************
* offset,binary
lbContPtr = FREAD(lnCardHandle,5)
DO llError
* offset, decimal
lnContPtr = Asc2Num(lbContPtr)
* Read the Topic *
******************
lcTopic = FREAD(lnCardHandle,47)
DO llError
ln0End = AT(CHR(00),lcTopic)
lcTopic = iif(ln0End=0, ;
lcTopic,left(lcTopic,ln0End-1))
* Reposition to the Contents & Read *
*************************************
IF lnContPtr+2 # ;
FSEEK(lnCardHandle,lnContPtr+2,0)
DO llError
ENDIF
lbContSize = FREAD(lnCardHandle,2)
DO llError
lnContSize = Asc2Num(lbContSize)
IF lnContSize # 0
lcContents = FREAD(lnCardHandle,lnContSize)
DO llError
ELSE
lcContents = ""
ENDIF
* Create the output record *
****************************
INSERT INTO (lcCardDBF) ;
(Topic
, ;
Contents) ;
VALUES
;
(lcTopic , ;
lcContents)
NEXT
=FCLOSE(lnCardHandle)
RETURN

ASCII-to-numeric conversion UDF

Asc2Num.PRG
* Asc2Num
* Convert multi-character hi-byte, lo-byte
* to decimal
PARAMETER tcString
PRIVATE lnNum
* Trim off ending CHR(00)'s
DO WHILE RIGHT(tcString,LEN(tcString)) = CHR(00)
tcString = LEFT(tcString,LEN(tcString)-1)
ENDDO
lnNum = 0
FOR i = LEN(tcString) TO 1 STEP -1
lnNum = lnNum + ;
ASC(SUBSTR(tcString,i,1))*256^(i-1)
NEXT
RETURN lnNum

Low-level file function error handler

* LLError.PRG
* Low Level Error Handler
PRIVATE ALL LIKE l*
IF FERROR() # 0
DO CASE && Determine which error
CASE FERROR() = 2
lcReason = 'File not found'
CASE FERROR() = 4
lcReason = ;
'Too many files open (out of handles)'
CASE FERROR() = 5
lcReason = 'Access denied'
CASE FERROR() = 6
lcReason = 'Invalid file handle given'
CASE FERROR() = 8
lcReason = 'Out of memory'
CASE FERROR() = 25
lcReason = 'Seek error'
CASE FERROR() = 29
lcReason = 'Disk full'
CASE FERROR() = 31
lcReason = 'General Failure'
OTHERWISE
lcReason = 'Unknown LL error '+ ;
LTRIM(STR(FERROR()))
ENDCASE
*** Display the error ***
WAIT WINDOW "Low level file error: "+ ;
lcReason NOWAIT
CLOSE ALL
CANCEL && you may want to return to master
ENDIF
RETURN

Windows Cardfile Format

The complete file format for Windows Cardfiles is in the Microsoft Knowledge Base as article #Q99340. The file formats for many types of data files are either documented by the manufacturer, or by third parties in books and magazines. Once the format is known, FoxPro’s low-level file functions let you go in and get the data.

Cardfile 3.0 Header

Bytes
Contents
0,1,2
Signature / version bytes, always "MGC" for 3.0 and for 3.1 files containing only text, "RRG" for 3.1 containing OLE
objects 
3,4
No. of cards, high byte, low byte format Cardfile 3.0 Index line
0,5
Reserved for future use, always CHR(00)'s
6-9
Pointer to card's body text
10
Always CHR(00), 'flag byte'
11-50
Index text (appearing atop card)
51
Always CHR(00)

 

Cardfile 3.0 Body

Bytes
Contents
0,1
length of body text (440 characters max)
2-xxx
body text

Ted Roche enhances Xbase systems and trains programmers in more efficient development techniques. He is a 1994 recipient of the Microsoft Support Most Valuable Professional Award. Ted has published several articles, contributed to two books on FoxPro, and was a technical editor for four FoxPro 2.5 books. He is co-editor of the Boston Computer Network News, and is a frequent speaker at user group and professional conferences. CompuServe 76400,2503.

Copyright 1995, 2017 by Ted Roche under a Creative Commons Attribution, Share-alike, Non-commercial license.

Powered by WordPress. Designed by Woo Themes

This work by Ted Roche is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States.