Using setItem()
If you want to exchange data, you must send (or in DDE terminology, poke) the data into another application or get information from it. To do this, open a DDE channel, use the setItem() method, and assign the data as a value to the DDE variable. If the README.DOC document has a LastName bookmark, you can set the item of conversation to it with the following statement:
1: ddeVar.setItem("LastName")
Otherwise, you can use syntax 3. For example, the following opens a conversation and sets both the topic and the item in one line of code:
1: ddeVar.open("WINWORD", "C:\\WINWORD\\DATA\\README.DOC", "LastName")
After you establish the link, or gateway, you can use regular dot notation to manipulate the data in the open document. For example, to get a value from the established DDE item and to put it in a variable, you can use the following code:
1: var
2: Last_Name String
3: endVar
4: Last_Name = ddeVar
Setting the value in the other application is just as easy. For example, the following statement sets the item in the other application to the value in the variable named Last_Name:
1: ddeVar = Last_Name
Now, put all these statements together. The code looks like the following:
1: ;button :: pushButton
2: method pushButton(var eventInfo Event)
3: var
4: ddeVar DDE
5: Last_Name String
6: endVar
7:
8: ddeVar.open("WINWORD", "C:\\WINWORD\\DATA\\README.DOC", "LastName")
9: Last_Name = ddeVar
10: Last_Name.view("Value in Word")
11: ddeVar = "Ault"
12: Last_Name = ddeVar
13: Last_Name.view("New value in Word")
14: endMethod
Exchanging Multiple Values
So far, this chapter has discussed about sending and receiving only single values. You might be wondering how you send multiple values. To do so, first use the setItem() method to set the item. Next, send or receive the value. Then, use setItem() again to establish a new item of discussion. In DDE terminology, that is a new item within the current topic.
The following code builds on the README.DOC example. It sends three different values to three different bookmarks. It assumes that you already have established the three bookmarks in Word for Windows:
1: ddeVar.setItem("LastName")
2: ddeVar = Last_Name
3:
4: ddeVar.setItem("FirstName")
5: ddeVar = First_Name
6:
7: ddeVar.setItem("PhoneNumber")
8: ddeVar = Phone_Number
Paradox makes asynchronous transmissions. This means that Paradox waits for the other program to respond. This waiting doesn't always occur correctly. This means you might need to put a sleep() command in your code to wait for the server to respond to certain requests. As a rule of thumb, if a DDE command fails when it sends or gets values or when it sends commands, put a sleep(100) command between the two lines of code. This tells Paradox to sleep for one-tenth of a second. In computer time, this is usually plenty of time for the other application to return a message.
Starting the Other Application Ahead of Time
If you want, you can launch an application before you establish the DDE link. For example, you can launch Word for Windows ahead of time with code such as the following:
1: ;Button :: pushButton 2: method pushButton(var eventInfo Event)
3: execute("WINWORD.EXE C:\\WINWORD\\README.DOC")
4: endmethod
This approach offers no real advantage. Sometimes, however, you want to enable the user to open the application ahead of time without doing any DDE exchanges.
The form in Figure 27.2 demonstrates how to link to Word for Windows, send and get values, and print. You must have Word for Windows in your path for this form to work. When you tell Word for Windows to print, the system doesn't switch to Word for Windows. Instead, the Printing dialog box from Word for Windows appears over Paradox.
Figure 27.2. APPS\INFO\INFO.FSL uses Word for Windows to spell check text.
You can use DDE to spell check a field with Word for Windows. Take a look at The Information Database (\APPS\INFO\INFO.FSL) for an example of this technique. Also note that you can use this technique to grammar check.
Sometimes, you might want to send a large text field to Word for Windows for spell checking. When the spell checking is done, you retrieve the text file. The form in Figure 27.2 demonstrates how you can do this. Table 27.2 lists sample DDE applications, names, topics, and items. For the applications, case doesn't matter.
Table 27.2. Sample DDE applications, names, topics, and items.
Program Application DDE Topic DDE Item ObjectVision Vision OVD filename Field name Paradox PDoxWin Table filename Field name Word for Windows WinWord Document Bookmark WordPerfect for WPWin Document not supported Win. v5 WordPerfect for WPWin60_Macros Document not supported Win. v6 Quicken for Quicken System SysItems, Windows ReturnMessage, Key
Limitations of DDE
DDE in Paradox is limited. You can't send values from another application to a Paradox form. You must use ObjectPAL to get the values from the other application. This is simply a matter of what has control. Paradox must have control, except when you use a TableView object. Another limitation is that Paradox doesn't support the system topic, which is a general-purpose topic that some applications support. Paradox can't exchange data through the system topic.
OLE Basics
The term OLE stands for object linking and embedding. You use OLE to insert files from OLE servers into a Paradox table using the OLE field type or to an OLE object on a form or report. An OLE client is an application that uses the documents provided by an OLE server. Paradox is both an OLE server and client. An OLE server is an application that can provide access to its documents by means of OLE. An OLE object is the object the server shares. This object is similar to the document that an OLE server can save. An OLE object is stored in the OLE client much as a document is stored on a disk. An OLE variable is a handle to an OLE object. You use an OLE variable in ObjectPAL to manipulate an OLE object.
Using OLE
OLE is another tool that enables you to take advantage of another application. OLE enables you to insert objects created by OLE servers into forms and reports, or you can use them in fields of a table. You can use OLE to manage large numbers of files on disk. For example, you might browse through a database of AutoCAD drawings, which is actually a Windows metafile snapshot of a data file. Then, double-click on the drawing you want. The OLE link automatically brings up AutoCAD and feeds it the correct file. OLE fields enable you to store objects from other Windows applications in your database. You can create a database of your word processing documents or spreadsheets and manage them with Paradox.
OLE Version 1
Currently, two types of OLE are on the market: object linking and object embedding. Can Paradox OLE embed, link, or both? You can link to files using the command-line option in Windows Object Packager or through DDE Paste Link into a table's alphanumeric field.
OLE Version Two
Paradox now supports version two of OLE. With OLE 2, you can use the other application right in your database application; including its menus.
OLE Controls
OLE Controls (or OCX controls) are new to Windows 95/NT. The OLE control is a 32-bit version of the old 16-bit VBX controls. OLE controls are portable to DEC Alpha, MIPS, Apple Macintosh, and PowerPC environments.
Because OLE controls are a separate application running as a subprocess of Paradox, you will find the behavior is different from normal Paradox objects. Each OCX has its own child window on the form and its own message queue. This gives the OCX its own event model.
ObjectPAL and OLE
Through ObjectPAL, you can use Paradox as a client or server. You can use ObjectPAL to send data from Paradox to another application or to get values from another application. In ObjectPAL, you can retrieve data in two ways: from a table or from the clipboard.
OLE Methods and Procedures
You can use the OLE methods and procedures and most of the methods and procedures from the AnyType type. Table 27.3 lists the OLE class of procedures and methods.
Table 27.3. OLE methods and procedures.
blank* canLinkFromClipboard canReadFromClipboard dataType* edit enumServerClassNames enumVerbs getServerName insertObject isAssigned* isBlank* isFixedType* isLinked linkFromClipboard readFromClipboard writeToClipboard unAssign* updateLinkNow *Inherited by the Anytype type
Table 27.4 describes some of the more important OLE methods.
Table 27.4. OLE methods and procedures descriptions.
Method Description canReadFromClipboard Reports whether an OLE object can be pasted from the Clipboard into an OLE variable. edit Launches the OLE server and enables the user to edit the object or take another action. enumVerbs Creates a DynArray that lists the actions supported by the OLE server. getServerName Returns the name of the OLE server for an OLE object. readFromClipboard Pastes an OLE object from the Clipboard into an OLE variable. writeToClipboard Copies an OLE variable to the Clipboard.
OLEAuto Methods
OLE Automation is a way to manipulate an application's objects from outside that application. OLE Automation uses OLE's component object model, but can be implemented independently from the rest of OLE. With OLE Automation, you can do the following:
- Create objects for programming tools and macro languages.
- Create and manipulate objects from one application exposed in another.
- Create tools that access and manipulate objects.
Using enumAutomationServers
The procedure enumAutomationServers() reads the registry on the current machine and gathers all the available OLE servers, with a programmable interface. Following is the syntax:
enumAutomationServers ( var servers Array[ ] String ) Logical :
You can type in the following code into the pushButon method of button to list all the available OLE servers on your machine:
;Button :: pushButton
method pushButton(var eventInfo Event)
var
dynServers DynArray[] String
endVar
enumautomationservers(dynServers)
dynServers.view("Available OLE Automation Servers")
endMethod
Using open, enumMethods, and version
The open, enumMethods, and version methods enable you to open an OLE server and extract information. Following is the syntax for each:
open ( const serverName String ) Logical enumMethods ( var methods DynArray[ ] String ) Logical version ( ) String
The following example opens the Paradox OLE server and enumerates its methods to a dynamic array. Finally, it displays the version number.
;Button :: pushButton
method pushButton(var eventInfo Event)
var
oa OLEAuto
dynMethods DynArray[] String
endVar
;Open server.
oa.open("Paradox.Application") ;Display methods
oa.enumMethods(dynMethods)
dynMethods.view("Paradox Methods")
;Display version.
view(oa.version())
endMethod
The OLEAuto methods enumObjects() and enumProperties() enable you to extract other types of information from OLE servers.
Some of these methods of an OLE control might not be accessible by ObjectPAL because their types are not supported, in which case the prototype will show an asterisk *.
Native Windows Controls
Paradox provides support for native window controls (NWC) to be used in forms. To ObjectPAL, native windows controls and OLE controls are virtually identical. Paradox has an OCX type wrapper inside of the Paradox form system so that native controls behave like OLE controls. All the OLEAuto programming concepts are applicable to native windows controls. The only difference (and benefit) is that there is no .OCX file (the code is stored in a the Paradox DLLs). Table 27.5 lists all the OLEAuto methods and procedures.
Table 27.5. OLEAuto methods and procedures.
attach close enumAutomationServers enumConstants enumConstantValues enumControls enumEvents enumMethods enumObjects enumProperties enumServerInfo first invoke next open openObjectTypeInfo openTypeInfo registerControl unregisterControl version
The New ^ Operator
Among the OLE automation methods included with Paradox 7 (such as FormOpen(), ScriptOpen(), and so on), is delete(), which deletes a named file. The following is an example of using delete() with an OLEAuto variable. var
var
oa OLEAuto
endvar
oa.open("Paradox.application")
oa.delete("customer.db")
In the above code, there is a conflict with the existing delete() method. You must use the new ^ operator (as in oa^delete()) which unambiguously identifies the method as an OLE method. An ObjectPAL method always overrides an automation method when using dot notation. Use the ^ to refer to automation methods in which there may be a conflict with an existing ObjectPAL method.
Using enumServerClassNames()
Use enumServerClassNames() to find the OLE servers on your system -- specifically, the OLE verbs. For example, a Microsoft Word for Windows Server name is either Microsoft Word 6.0 Document or Microsoft Word 6.0 Picture. The corresponding OLE verb you use in ObjectPAL to refer to the OLE server is either Word.Document.6 or Word.Picture.6. You find this information with the following code:
1: ;btnServers :: pushButton
2: method pushButton(var eventInfo Event)
3: var
4: dyn DynArray[] AnyType
5: o OLE
6: endVar
7:
8: o.enumServerClassNames(dyn)
9: dyn.view("OLE Servers on system")
10: endmethod
Type the preceding code into the pushButton method of a button on a form. Run the form and click the button.
Using insertObject()
You can use insertObject() to insert a linked or embedded OLE object into an OLE variable. Following is the syntax for insertObject().
insertObject ( ) Logical insertObject ( const fileName String , const link Logical ) Logical insertObject ( const className String ) Logical
The first syntax is just like choosing Edit | Insert Object. In the second syntax, you insert the name of a file. Finally, in the third syntax, you specify the class of the object to insert. For example, place a button and an OLE object on a form and type the following into the pushButton method of a button:
1: ;btnInsertOLE :: pushButton
2: var
3: o OLE
4: endVar
5: method pushButton(var eventInfo Event)
6: if not o.insertObject() then
7: errorShow()
8: fldOLE.value = o
9: endIf
10: endMethod
Using OLE Type edit()
The edit() method launches the OLE server application and gives control to the user when used with an OLE object. The argument oleText is a string that Paradox passes to the server application. Many server applications can display oleText in the title bar. For example, the following is the syntax template for OLE :: edit.
edit ( const oleText String, const verb SmallInt ) Logical
edit passes verb to the application server to specify an action to take. verb is an integer that corresponds to one of the OLE server's action constants. The meaning of verb varies from application to application, so a verb that is appropriate for one application may not be for another. Usually, you can pass an OLE server the number 1 to play or display the OLE object.
Using CanReadFromClipboard()
With ObjectPAL, you can read OLE data directly from the Windows Clipboard. You can use canReadFromClipboard() to test whether anything is on the Clipboard. The syntax is as follows:
canReadFromClipboard ( ) Logical
This method is useful in a routine that informs the user whether an operation is possible. canReadFromClipboard() returns True if an OLE object can be read from the Clipboard into an OLE variable; otherwise, it returns False. For example, type the following into the pushButton method of a button:
1: ;Button :: pushButton
2: method pushButton(var eventInfo Event)
3: var
4: o OLE
5: endVar
6:
7: view(o.canReadFromClipboard)
8: endMethod
Another technique is to use the timer event method and check the result of enumWindowNames(). As an alternative to enumWindowNames(), you could use the Windows API FindWindow function for the OLE server's window title. When it is gone, kill the timer and assign the OLE variable
Summary
In this chapter, you learned about DDE, OLE, OCX and the new OLEAuto type. All provide easy ways to exchange data with another Windows application. Although DDE and OLE are primarily interactive user features, you can carry out some interesting tasks with them in ObjectPAL. The primary difference between DDE and OLE has to do with what each one sends. DDE sends data and commands between two applications. OLE either embeds data directly into another application, or stores information about a link to a specific piece of data. OCX is an extension of OLE you can use to extend the objects in Paradox.