Special Edition Using Visual FoxPro 6


Chapter 20

Introduction to COM


What is COM?

Microsoft has greatly enhanced support for COM in the latest version of Visual FoxPro (6). COM can greatly change the way you look at applications, but it is relatively simple to learn and use. This section of the book relates to the Component Object Model (COM), Microsoft's standard for inter-application communication.

This chapter introduces COM and discusses how you can use it to enhance Visual FoxPro applications. Chapters 21 and 22 will go into practical issues regarding using Visual FoxPro as a COM client and COM server, respectively.

COM is a standard for communicating between objects. The basic idea is to have a standard that says objects will accept and send messages in a particular way, thus enabling objects written in disparate languages to communicate with each other as if they were written in the same language.

The bottom line is that any COM object can communicate with any other COM object. So, in your Visual FoxPro applications, you can communicate with other objects written in Visual Basic, Visual C++, and so on.

Some COM objects are visual in nature. Any ActiveX control that ships with Visual FoxPro (such as the Calendar control) is a COM object. Although it was not written in Visual FoxPro, it has been designed to work with any application that complies with the COM standard for containing visual COM objects.

Other COM objects have no visual characteristics at all. For example, you can use a COM object designed to do highly specialized pricing calculations for a brokerage firm. The COM object can be written by a group responsible for creating pricing algorithms for the company (a highly specialized and sensitive field) and used by any application within the brokerage firm. This pricing object would have methods and properties that are used to store, retrieve, and process information, but no need to have a GUI at all.

Finally, some objects are parts of full applications in and of themselves, with portions "exposed" for the outside world to use. These applications have been designed not only for use by end users but also by developers. For example, a faxing application can have objects exposed for use so that a developer can automate faxing a document. Microsoft Office applications have portions exposed so that a developer can use the applications by "remote control."

A Little Background

Back in the "good ol' days" of DOS, applications had to have all their required functionality built in. Remember the old DOS printer drivers in FoxPro 2.x for DOS? This is a prime example. WordPerfect and Word also did the same kind of thing. Because DOS provided nothing in terms of operating system level support for such tasks as printing, the software had to provide all the functionality itself.

Needless to say, this led to a huge amount of duplication. All three applications just mentioned had printer drivers built in, but each had its own version of printer driver software. There was no commonality between the efforts and each company had to do the same effort on its own.

The advent of Windows changed that. Given some operating system support, standards were put in place for communicating with the operating system and enabling the operating system to do the rest. After Word, FoxPro, and WordPerfect migrated to Windows, their own versions of printer drivers went away.

However, Windows applications were still fairly monolithic. Monolithic applications are applications that have everything built in to them. With the advent of Windows, certain operating system level functions were standardized, but business functionality (such as word processor features that enabled you to write letters to clients) were still the realm of each application.

Then came Dynamic Data Exchange (DDE), which enabled one application to take control of another. In effect, you sent commands, instructions, and data from one application to another. The operating system had a built-in means of channeling the commands from the DDE client application to the DDE server application. In order for an application to be DDE-compliant, it had to conform to the rules for accepting and sending DDE instructions and information. Basically, if you followed the rules, you got to play with the other applications in the DDE sandbox.

The interaction afforded by DDE was rather kludgy to work with, but it did enable some flexibility. A developer no longer had to create a mini-word processor to generate letters, because the job could be passed on to some DDE-compliant word processor. All that was required from the developer was to learn how to control the DDE server application (no small task, by the way).

Then object orientation entered the picture and changed the way we look at software and business problems. As was discussed in Part IV, "Object-Oriented Programming," an object-oriented application is built based on discrete entities called objects. In their coded form, objects are called classes.

The nature of an object-oriented application is to build a group of distinct classes that interact as necessary to get the job done. If you think about it, this is not all that different from putting a team together to complete a project. Each member of the team has his or her own distinct set of responsibilities. However, in order to complete their work, the team members interact with each other. They pass information back and forth (for example, the GUI designer might work with the data designer to know what fields are used as control sources) and, in general, cooperate to complete the work. The same is true of an object-oriented application. Each class is a member of the team, and the classes collaborate to get the job done.

COM finally brings the whole issue of monolithic applications to closure. Gone is the kludgy means of inter-application communications represented by DDE. Now, you can use classes built into other applications directly. Instead of controlling an application by remote control, COM enables you direct access to the application. In effect, your applications get to run the same code as the COM server applications. In the rest of this chapter and throughout this section, you will see that the use of COM servers is practically transparent in the code.

Finally, disparate applications can use each other's functionality, thereby saving time, money, and resources.

What Does COM Represent?

COM, as an idea, is revolutionary in the realm of development strategies. Once upon a time, each application had to have all its functionality built into itself. That is no longer the case. We have entered an age of specialization, not only in terms of technology and development language but also in terms of business knowledge.

Accounting applications are a good example. For the most part, nuts and bolts accounting principles do not change. Yet, every time you write an application with accounting functionality, you have to either create it yourself or modify something else. If you're lucky, you get to write a module that runs "parallel" to the accounting software.

Now, let's face it. Writing accounting functionality is a bear. A ton of work goes into it. The minute you hear terms such as "General Ledger" and "Accounts Receivable," the time estimate to create an application jumps. I personally know of no people who look forward to writing a General Ledger module for their clients, especially when there are 50 gazillion packages out there that handle accounting.

So, what do you do? Many of us purchase an accounting package such as SBT and do either of the following things:

Neither means of accomplishing the task is necessarily the best.

Suppose one accounting software package was created to be COM compliant. The software package could give all the nuts and bolts accounting and expose the necessary functionality for interfacing with the various modules. Now all a programmer has to do is write his custom GUI and processing but leave the accounting up to the accounting software. And, because the accounting software is COM compliant, it can serve as the basis for thousands of applications without having to change one line of the accounting software's code.

Consider another example in which the pricing model mentioned earlier would be key. The pricing models created by the bank I used to work for on Wall Street were custom-designed by the bank. Pricing models are crucial to the success of the bank because they tell it what to charge customers for certain financial deals. However, although the pricing models might be used across many different business units, frequently the units use their own software, which might be written in different languages.

COM can play a huge part in this situation in two ways. First of all, for the bank, a department can create pricing model objects using COM. Business units can create software to their heart's content, with custom GUIs and data structures, and still use the bank's pricing model. Second, COM can play a huge role for the software vendors, too. A software vendor can write an application that is designed to use a COM object for pricing. It can then sell the application to the banks, saying, "Use our software and you can still use your own pricing models if you follow these standards."

The power and flexibility increase for an organization is potentially huge.

Scalability with COM/DCOM

Because COM objects are, in effect, applications themselves (or portions of applications), they exist as separate files (.EXE or .DLL) on the hard drive. One of the nicest things about COM is that you can place the .EXE or .DLL on another computer on the network and run the COM object on that other computer and still access the results on the client computer.

Let's take a moment to look at this.

In a conventional network situation, programs sit on the network but run on the local workstation. That's not what I am discussing here. I am looking at being able to execute a program, in the form of a COM object, on another computer, and have the program use the memory and processor resources of that other computer while it is being accessed from the local workstation that made the call.

This is called Distributed COM (DCOM). Imagine the flexibility this introduces when scaling an application. If an application has grown in terms of the number of users, the COM components can be distributed around the network, thus enabling a fairer distribution of the processing load.

So, What Do I Need to Learn?

Not much. If you're used to using objects, methods, properties, events, and the like, you will fit right in with COM. The only difference here is that you do not create these objects, someone else does. If you think about the difficulties that arise from using someone else's code, the difficulties in using COM are not that different. The biggest difficulty lies in not knowing which methods and properties the COM server supports. If the COM server is well documented, though, you should be okay on that score.

How to Use a COM Object

You use a COM object just like any other object. You use CreateObject() to get an object reference. The only trick is to know the name of the class to instantiate. For Excel, the class is Excel.Application. For Word, use Word.Application.

When you issue a CreateObject(), Visual FoxPro does some checking to find the name of the class to instantiate. Here's the order in which Visual FoxPro looks for the name of the class:

After you have an object reference, you use the object as if you were dealing with a class
created in Visual FoxPro.

By the way, note the difference in the name of the class to instantiate. You are probably used to one-word class names. With COM, you have two-word class names. The first word represents the application and the second represents the class within the application to access.

TIP
If all this seems to be very similar to a topic called OLE automation, you're right. OLE automation is part of COM. So, if you see OLE, think COM.

How Exactly Does COM Work?

When you install an application that is a COM server, it registers itself (and all its classes) in the Windows Registry. For example, look at the Windows Registry entries for Visual FoxPro (which is a COM server).

Under HKEY_CLASSES_ROOT there is a branch named Visual.FoxPro.Application.6. Within that branch are numerous sub-branches. The branch to take note of at this point is

HKEY_CLASSES_ROOT\Visual.FoxPro.Application.6\CLSID

A CLSID (Class Identifier) is a unique number (called a GUID or Global Unique ID) that identifies that class. Microsoft assigns the CLSIDs and guarantees uniqueness. There is a separate entry for Visual FoxPro's GUID (which is, by the way, {008B6020-1F3D-11D1-B0C8-00A0C9055D74}) in the Registry. The key for that is

HKEY_CLASSES_ROOT\CLSID\{008B6020-1F3D-11D1-B0C8-00A0C9055D74}

This tree in the Registry has all the information you need to get to the class you need. For example, the LocalServer32 branch has the path to Visual FoxPro.

When the client application issues a CreateObject() with the name of the COM class (in this case, Visual.FoxPro.Application.6), the client asks the operating system for an instance of the requested object. The operating system looks in the Registry to find the GUID of the COM object. The Registry has the location of the COM program file (either .EXE or .DLL). The OS then knows where to go to instantiate the object. After it instantiates the object, it returns a reference to the calling application and the process is complete.

Once instantiated, you can use methods and properties of COM objects as if they are native to your development environment.

TIP
If you're not sure what the COM class name is (and you do not have the application's COM documentation), look through the Registry and see what's there. For example, Excel has an .Application entry. You will typically find something useful there.

Understanding Type Libraries

A type library is basically documentation. It documents the collections, objects, methods (with their parameters), and properties (with their data types) that are exposed for use by COM client applications. All COM objects have type libraries.

Some development products, such as Visual Basic, use the type libraries to give online, in-line assistance. For example, when calling a method in a COM object, Visual Basic displays the expected parameters automatically.

Visual FoxPro does not use type libraries quite to that extent. You will learn more about type libraries and Visual FoxPro in Chapter 21, "Visual FoxPro as a COM Client."

A type library can be a part of an object file, in which case it will have an extension of .OLB. If a type library is a standalone file, it has an extension of .TLB.

Visual FoxPro COM Capabilities

At this point, you have learned the theory behind using COM with Visual FoxPro. Now, let's take a tour of Visual FoxPro's COM capabilities, commands, and functions.

CREATEOBJECT(ClassName)

As stated earlier, CreateObject() is the mechanism through which you create an instance of a COM object. One crucial point to note is that CreateObject() always creates a new instance of the COM server.

Try this. From the Command window, issue these commands:

ox=CreateObject("Word.Application")
oy=CreateObject("Word.Application")

Now, bring up the Close Program dialog in Windows 95/98 by pressing Ctrl+Alt+Del. In NT, you need the Task Manager: press Ctrl+Alt+Del and select Task Manager from the dialog. Look for WinWord and count how many times it's running. You'll see two (assuming you are not running WinWord someplace else).

Wait. It gets worse. Switch back to Visual FoxPro and release the variables. You can even quit Visual FoxPro. Check again to see how many instances of WinWord are running. You guessed it: both instances are still there. In order to get rid of an unwanted instance, you need to issue the Quit method for WinWord.

You need to be careful with this because each instance of a COM server takes memory.

If you want to get a reference to an already running copy of a COM server, use GetObject().

GETOBJECT(FILENAME,CLASSNAME)

GetObject() is similar to CreateObject(). It accepts two parameters. The first is the name of a file to open and the second is the class name to work with.

GetObject() has two uses:

These uses are discussed in the following sections.

Referencing an Existing File  First of all, GetObject() can be used to open a file that the OLE Registry knows about.

Suppose, for example, you want to open BUDGET.XLS, which resides in the root directory of your C: drive. You have two options. Your first option is to create an instance of the Excel COM server using CreateObject() and then open the file manually with a call to a method of the Excel object.

GetObject() offers a one-step approach:

oExcel = GetObject("c:\budget.xls")

Here, you are simultaneously instantiating an Excel object, opening the file, and getting a reference to it.

Notice that you do not have to tell Visual FoxPro that the file is an Excel file. Windows knows that an .XLS file is an Excel spreadsheet and that's good enough.

TIP
If you are dealing with an ambiguous situation (such as a file that is a particular type of document but the normal extension has been changed), you can specify the appropriate class name in the second parameter.

The cool part of all this is that GetObject() is smart enough to know if there is an instance of the server running. If the server is already running, it doesn't create a new instance; it uses the one already running.

Getting a Reference to an Already Running Server  If you just want to get a reference to an already running server, use GetObject() like this:

oExcel=GetObject(,"Excel.Application")

By only specifying the object, GetObject() shoots for getting a reference to the already running application. If the application is not running, an OLE error is displayed.

In Chapter 21, you'll see a generic means of getting a reference to an application that prevents an extra instance of applications.

SET OLEOBJECT

Visual FoxPro, by default, will look in the Windows Registry if it cannot find the class specified in a CreateObject() statement. This can be turned off with SET OLEOBJECT. In effect, SET OLEOBJECT turns off the capability to use COM objects.

Why would you do this? Well, the main reason is time. When Visual FoxPro looks in the Registry, it first has to load OLE support in case it is instantiating a COM object. This takes time and resources. The search through the Registry takes time, too. If you know that you are not using COM in your application, issuing SET OLEOBJECT OFF will save some time and resources.

If you're worried about ActiveX controls (or OLE objects you have in general fields), don't be. Because Visual FoxPro knows that these objects are, by definition, OLE objects, Visual FoxPro always loads OLE support before opening them.

For all intents and purposes, SET OLEOBJECT only affects behavior when you are trying to get an object reference in code.

One more point: GetObject() is designed to get a reference to an existing OLE object instantiated in Windows. Because SET OLEOBJECT OFF turns off OLE support, GetObject() returns an error when SET OLEOBJECT is OFF.

ComClassInfo(oObjec>,nInfoCode)

ComClassInfo() is designed to return some information from the Registry about an already instantiated COM object. oObject is the object reference, and nInfoCode is a numeric parameter indicating which information is requested, and has values described in Table 20.1.

Table 20.1  Description of the Information Returned for Values of nInfoCode
nInfoCode
Information Returned
1 (Default)
The object's "programmatic identifier" (ProgID). A ProgID is a Registry entry that can be associated with a CLSID; think of it as a name associated with the CLSID. Each version of the program has its own identifier. For example, Visual FoxPro has ProgIDs of Visual.FoxPro.Application.6 (for 6) and Visual.FoxPro.Application.5 (for 5.0). A ProgID is also a class name that can be used to instantiate the COM server. For example, you could issue CREATEOBJECT for either 5.0 or 6 using the ProgID as follows:
oVFP5 = CREATEOBJECT("Visual.FoxPro.Application.5")
oVFP6 = CREATEOBJECT("Visual.FoxPro.Application.6")

Typically, however, you don't want to be version-specific in a class name. You usually aren't interested in instantiating Visual FoxPro version 5 or 6, but rather want the latest version that's installed on the machine. You want to ask for a reference to Visual.FoxPro.Application and have the Registry determine what the latest version is and instantiate that class for you. That's the VersionIndependentProgID.

2
The object's VersionIndependentProgID. The VersionIndependentProgID associates a ProgID with a CLSID. It is used to determine the latest version of an object application, refers to the application's class, and does not change from version to version.
3
An English name for the object, referred to as the friendly name.
4
The object's class identifier (CLSID).

Here are the return values for a reference to Excel.Application for Excel 97:

1
Excel.Application.8
2
Excel.Application
3
Microsoft Excel 97 Application
4
{00024500-0000-0000-C000-000000000046}

CreateObjectEX(CLSID | ProgID, ComputerName)

As discussed earlier, DCOM means that the COM server application exists and runs from a different machine on the network. Normally, the client machine is configured and told where the COM server application is and a standard CreateObject() will do the trick. CreateObjectEX() removes the need for special configuration and enables you to specify the machine to use.

This can be useful if you want to distribute the load for an application among multiple machines on the network.

CreateObjectEX() requires Windows NT 4.0 or later, or Windows 95/98 with DCOM installed.

NOTE
In case you're wondering, CreateObjectEX() can only be abbreviated to 13 characters, which gives you CreateObjectE(). If you want my opinion, don't bother.

ComArray(oObject [,nSetting])

As Visual FoxPro programmers, we are used to arrays whose first element is element 1. However, VFP is not necessarily standard in this regard; many languages expect arrays to start with element 0.

The ComArray() function enables you to designate the manner in which arrays are passed to a specified COM object. Basically, you are registering a behavior with Visual FoxPro to indicate how a particular instance of a COM server should be handled.

The function accepts two parameters as follows:

oObjectThis is a reference to the COM object being queried or changed.
NsettingThis is an optional numeric setting to designate how you want arrays to be passed to the COM object. Table 20.2 describes the various possible settings. If nSetting is not provided, the function returns the current setting for the specified COM object.

Table 20.2  How Arrays Are Passed to the COM Object for Various Values of nSetting
nSetting
Description
0
The first element in the array is zero (0) and the array is passed by value to the COM object.
1 (Default)
The first element in the array is one (1) and the array is passed by value to the COM object. This setting is compatible with earlier versions of Visual FoxPro.
10
The first element in the array is zero (0) and the array is passed by reference to the COM object.
11
The first element in the array is one (1) and the array is passed by reference to the COM object.

Notice that ComArray() is only used when arrays are passed to COM objects using the following syntax:

oObject.Method(@laArray)

If the @ token is omitted, only the first element of the array is passed to the COM object and ComArray() has no effect.

Visual FoxPro as a COM Server

So far, you have learned Visual FoxPro using other COM servers. But the power of Visual FoxPro and COM certainly doesn't stop there. You can create your own COM servers using Visual FoxPro. As you'll see in Chapter 22, you can do all kinds of wonderful things with Visual FoxPro COM servers. But first, Chapter 21 continues the discussion of working with Visual FoxPro as a COM client by looking at some practical issues and how you can enhance the functionality of your Visual FoxPro applications by using other applications such as those in the Microsoft Office suite.


© Copyright, Sams Publishing. All rights reserved.