You are on page 1of 24

Understanding OLE

By Ken North
DBMS, June 1995

Microsoft's language-independent, binary standard for object sharing on desk-tops and


across networks.

The maturation of the computing industry has some parallels with agri-business, where,
in developed nations, a few producers supply the needs of many consumers. One widely
accepted view of our computing future is that we will eventually become a community
with a few producers (object implementors) supplying many consumers (object users).
The consumers will be programmers and power users who will build applications by
drawing from a stock of interoperable objects before writing custom code or building
custom objects to fill in functionality gaps. The Microsoft contribution to this view of
computing with shared objects, or component objects, is Object Linking and Embedding
(OLE). [For a complete list of the acronyms used in this article, please see Table 1.]

OLE provides a language-independent, binary standard for object sharing. Before


jumping into this technology, you should understand OLE, the Component Object Model
(COM), services that build on COM, and OLE custom controls (OCXs). COM is a core
technology for creating shareable binary components. It provides the infrastructure upon
which OLE layers other software services. COM is a language-independent binary
standard that permits object sharing by applications written in a variety of languages.
OCXs are Windows controls that provide data-binding capabilities that make them
particularly suited to database work. In addition to these technologies, Microsoft has
launched an initiative that creates a data integration layer over COM. (See the sidebar,
"Nile, Microsoft's Data Integration Initiative".) In this article, I look at each of these
topics in more detail.

Microsoft is working on an update to the current OLE 2.0 that will provide interoperable
and distributed objects. The company released a revised specification for OLE's COM in
March 1995. This specification update discusses distributed objects based on Open
Software Foundation's Distributed Computing Environment (DCE) Remote Procedure
Calls (RPCs). The COM network protocol is based on the 1994 X/Open DCE RPC
Common Applications Environment (CAE) specification. Distributed OLE adds new
interfaces related to remote objects, but it is basically an upward extension that shouldn't
break existing OLE code.

Microsoft considers OLE a precursor to Cairo, its next-generation operating system that
will expose operating system services as objects. Other interoperable object solutions that
have become prominent recently include the Object Management Group's Common
Object Request Broker Architecture (CORBA), Component Integration Lab's OpenDoc,
IBM's System Object Model (SOM), and SunSoft's Distributed Objects Everywhere
(DOE). OLE 2.0 has a significant following within the developer community, even
though proponents of other technologies such as OpenDoc and CORBA argue that these
solutions are technically superior to OLE. CORBA enjoys support in Unix environments,
while OLE has become a de facto object standard for Windows computing.

The Rationale for Object Sharing


Some automobile drivers prefer the direct control of a four- or five-speed transmission
while others prefer the ease of an automatic transmission. Some programmers work with
call-level interfaces (CLIs) such as OSF/DCE or the Windows API; other developers look
for object solutions to simplify programming. This latter demographic is the focus for
software giants such as IBM and Microsoft, who recognize that capturing the support of
this developer group is critical to capturing market share in the 21st century. Object-
sharing technology is integral to the architecture of operating systems currently in
development.

A developer's choice of programming techniques often involves the level of abstraction at


which to operate. Object techniques raise the level of abstraction by encapsulating low-
level programming details. For example, a programmer working at a lower level will dig
into DCE's 400-plus system calls to use RPCs. A programmer working at a higher level
uses a technology such as OLE, which builds on DCE. OLE encapsulates functionality
that lets a developer deal with an object without concern for whether the object is local or
remote. OLE objects are defined in a registry, so they are usable across applications and
available to scripting languages such as Visual Basic for Applications. Microsoft predicts
that programming with OLE interfaces will eventually supplant most detail-level
programming in Windows and Cairo applications (much as assembly language gave way
to higher-level languages).

To understand OLE you should know a few terms and concepts (and a slew of acronyms,
as you may have noticed from Table 1). A container is an entity that contains linked or
embedded objects. Users of OLE container applications create and manage compound
documents that consist of objects from multiple source applications. Objects linked to or
embedded within other objects are nested. An application that creates and manages
compound documents is a container application. Server applications are object
implementors because they create and maintain Windows objects. OLE uses several types
of identifiers to define interfaces and object classes uniquely. The universal identifier is
the globally unique identifier (GUID), a 128-bit value that provides a unique identity for
each interface and object class. OLE also uses a class identifier (CLSID) for every
standard object class, and an interface identifier (IID) for every standard interface.
Windows Objects are component objects with a unique CLSID. The registration database,
or registry, contains the inventory of available objects. It includes a unique CLSID entry
for each component object class.

COM and Service Layers


If you understand the rationale for objects, you are probably wondering what services
OLE provides to application developers. The short answer is that OLE provides services
for object sharing and component integration. The heart of OLE is COM, which is the
foundation upon which Microsoft built additional services. Figure 1 shows the various
OLE services, which build on COM. COM provides memory management, error
handling, interface negotiation, interprocess communication, and other basic services for
component object management. To use OLE, you must install a variety of Windows
dynamic link libraries (DLLs), including compobj.dll, the DLL that supports COM.

When you instantiate a component object, you receive a pointer to the object's interface.
OLE objects implement one or more interfaces that provide access to an object's member
functions but not its data. Figure 2 illustrates multiple interfaces for a single object. The
instantiation of an interface is an array of pointers to member functions (the
implementation of the interface). An interface is analogous to a C++ abstract base class or
a collection of methods. Applications use OLE services by accessing the interfaces
defined in Table 2. By calling the interface, a program can obtain a pointer to a table that
contains an entry for each function available through that interface. The table is a virtual
table (vtable or VTBL) whose entries are themselves pointers to functions.

OLE supports dynamic binding, so you can determine at runtime what functions an
interface provides. To support this type of programming, every object implements an
interface called IUnknown, which provides a standard member function called
QueryInterface. Instead of resolving addresses and type information at link time, an
application can obtain this information from QueryInterface at runtime.

All OLE interfaces are derivatives of IUnknown; therefore, every object interface
supports QueryInterface. The notation for an OLE call is similar to C++, where you
specify a classname::member function. For OLE, you use an interface::member function
so that an application that wants to save text to a file would call IPersistFile::Save, where
IPersistFile is the interface and Save is the member function (or method).

COM uses a client/server model in which object users are clients and object
implementors are servers. In-Process servers execute within the client's address space,
while Out-of-Process servers are standalone executables. Clients and servers use a class
factory, a feature within the server module that creates objects. Client access to the class
factory occurs via the server's IClassFactory interface. COM client applications use the
same process to instantiate an object whether the server is In-Process, local, or remote.
The client passes a CLSID to COM when instantiating an object. To ask the class factory
to manufacture one or more objects, the client obtains the pointer to IClassFactory and
calls CoGetClassObject. The client can also call COM's CoCreateInstance to create a
single instance (one object).

Marshalling, the process of passing function calls and parameters across process
boundaries, handles differences related to different word sizes. It enables objects having
16-bit parameters to interoperate with those having 32-bit parameters. COMPOBJ.DLL
contains the marshalling code that resolves addressing differences, so that object users
see an object that matches their own address space and has the correct byte ordering.

Marshalling is handled by components called proxy objects. Unmarshalling is handled by


stub component objects. Every interface has its own proxy object to package method
parameters for that interface. OLE uses Lightweight Remote Procedure Calls (LRPCs)
for marshalling local objects, and DCE-compliant ORPCs ("object" RPCs) for distributed
objects. OLE uses an object description language (ODL) and type libraries (TLBs) to
describe objects. The text descriptions in ODL are compiled to produce the TLB, which is
an OLE compound document file that contains descriptions of types, modules, and
interfaces.

OLE provides more than basic component object management. The additional services
that build upon COM include Structured Storage, Monikers, Uniform Data Transfer, Drag
and Drop, Linking, Embedding, In-Place Activation, and Automation. The OCX
architecture adds Controls, Property Page, Property Change Notification, Events, and
Connectable Objects. OLE implements a standard set of protocols for performing a
variety of data-transfer operations, including drag-and-drop, clipboard control, and
compound document processing. Uniform Data Transfer simplifies data transfers and
change notification (for example, clipboard cut-and-paste operations). In-Place Activation
lets users edit, display, record, and play data without switching to a different window. A
Moniker is a Windows Object that describes the data source and includes code for data
binding. OLE includes five Moniker classes: Composite, File, Item, Anti, and Pointer.
Linking places only rendering information and a pointer to an object's data in a
compound document. Embedding places the rendering information and the object data
within the document. It is possible to edit an embedded object in-place or within its own
container, because the compound document includes a copy of the original embedded
object and all of the information needed to manage the object.

Automation and Structured and Persistent Storage


OLE Automation lets applications create and expose command sets for interapplication
operations. Applications can operate each other without human intervention, and
developers can write commands and macros that operate across application boundaries.
Automation lets object users manipulate objects by setting properties or calling methods
with optional arguments. An interface called IDispatch allows runtime binding to object
methods and properties.

OLE supports a model for structured storage that uses compound files instead of
traditional file-system interfaces, which use handles. OLE's storage.dll provides services
for compound files, making it the forerunner of the future Windows file system. OLE
Structured Storage includes two object types: streams and storages. Storage objects are
similar to directories containing other storage and stream objects. Streams contain
unformatted or unstructured data. Storage objects and stream objects support direct data
access and a transaction mode for committing changes to data. Objects that are stored
using a persistent medium, such as a magnetic disk, are persistent objects. OLE provides
several interfaces for persistent objects (such as IPersistFile, IPersistStorage, and
IPersistStream). Component objects can save their persistent state by implementing
IPersistStream and IPersistStorage.
Plug-In Components
The third-party market for plug-in components may speed OLE's adoption by Windows
programmers. The 16- and 32-bit OCXs are likely to succeed the 16-bit Visual Basic
Extension (VBX), the most widely used component software today. OCXs are actually
In-Process servers that support in-place activation and OLE automation. OCXs brought
new capabilities to OLE, including outgoing interfaces and an event model (which
associates events with the code that handles those events) to support event-driven
programming. Containers use "outgoing interfaces" to activate their OLE custom controls
in response to events.

Distributed OLE
With distributed OLE, Microsoft intends to support partitioning in order to enable
applications to use local or remote components. COM provides location transparency, so
an object's programming interface won't change, whether it is local or remote. OLE does
the work behind the scenes, and uses ORPCs for remote objects and LRPCs for local
objects. To make remote operations transparent, COM disables features that would be
appropriate for In-Process objects but are inappropriate for Out-of-Process objects.

Microsoft built distributed OLE on a DCE foundation. It uses the DCE security model,
naming conventions, and directory services. Also, the OLE Interface Definition Language
(IDL) is an extended version of the DCE IDL. Distributed OLE uses DCE RPCs and
supports the use of custom RPCs, as long as they are DCE-compliant. It also handles
marshalling for objects based on a variety of CPUs. Jeff Alger, one of Microsoft's OLE
gurus, describes the next version of OLE as being a "DCE implementation with value-
added for COM. It supplements DCE; it doesn't reinvent it."

COM uses proxies and stubs to support object location transparency. Whether an object
server is local or remote, it creates a proxy object to act as an In-Process object in the
client. The proxy object then talks to stubs that run in the server. A COM component, the
Service Control Manager (SCM), manages the connections to remote servers, so that the
server is readily accessible when a client issues a request. SCM keeps a database of class
information based on registry information. The SCM will return file path information,
start an executable, and return a pointer or forward the request to--and maintain--an RPC
connection with the remote SCM.

OLE also lets you override location transparency with custom marshalling. This
capability provides additional control when the separation of interface and
implementation introduces too much of a performance penalty across a network.

Error Handling and Debugging


Because OLE's error-handling is language-independent, it is illegal to throw an exception
across an interface. (An application signals an error condition by throwing an exception.
An error handler uses catch to state that it handles certain types of exceptions.) Instead,
COM interface functions use return codes to indicate errors. COM defines an HRESULT
type, functions, and macros to handle and report errors. HRESULT is a long integer that
consists of 13 bits for facility information and 16 bits for an error code. The facility code
identifies a grouping such as dispatch errors, RPC errors, Win32 errors, persistent-storage
errors, and so on.

The COM Network Protocol and the COM Library support debugging libraries at either
client or server, or on both sides of a COM invocation. The debuggers can use hooks at
either end of the RPC infrastructure to invoke debugger actions. Also, COM RPC
debugging can use Windows NT and Windows 95 facilities to spawn a debugger when an
application faults.

DBMS Interfaces Using OLE


The typical programming interface for database applications today is a CLI in which the
functions that provide database services reside in a separate library. To provide OLE-
based database access, some companies create an object layer that encapsulates a DBMS
CLI. Oracle Corp.'s Oracle Objects for OLE encapsulates the Oracle Call Interface (OCI),
and Sylvain Faust Inc.'s SQL Sombrero encapsulates SQL Server's DB-Library and
Sybase Open Client's CT-Library.

Microsoft Access and Visual Basic feature Data Access Objects (DAO), a collection of
objects (such as Databases, Recordsets, and QueryDefs) that work with a variety of
databases. The application uses a common programming model to operate against data
that may be local or remote, desktop or server, ISAM or SQL. Today, DAO is an
application-level object layer, but Microsoft's Nile will extend the scope to make objects
available across applications and computers. My article "Multidatabase Development"
(DBMS, October, page 76) provides more information about DAO.

Database developers are often justifiably concerned about committing resources to


technologies that fall out of favor. SQL, ODBC, and the new ANSI/ISO CLI represent
standards, but because object interfaces and visual controls are new technologies, some
developers worry that they'll have to throw away code. Oracle Objects for OLE is one
example of how code can be preserved while migrating to the new technology. Oracle
Objects consists of visual controls and an object interface for Visual Basic that replaces
the standard VB data control, but does not require major application recoding.

Implications for Client/Server


Today's client/server systems use stored procedures and triggers to implement rules.
SQL3 (the next version of the ANSI SQL standard) uses the term "persistent stored
modules" to describe procedures. OLE adds the flexibility to use objects at the client, and
distributed OLE lets you use objects at the server or on other machines. Distributed
objects provide more opportunities for logic distribution than the architectures that we
describe now as "two-tier" and "three-tier" client/server systems. For example,
repositories and rule-based servers could become factories for rules agents that operate at
all levels of a distributed computing environment.

OLE has the potential to enable tools such as VB and Powersoft Corp.'s PowerBuilder to
use shared objects to implement rules and provide access to repository information.
Today, PowerBuilder provides extended attributes and event scripts to validate input, and
VB provides methods for data validation, but client/server developers using those
products often rely on server stored procedures to centralize more elaborate rules. OLE
lets developers encapsulate rules in objects that are accessible to PowerBuilder, VB, or
other OLE-enabled applications. For example, you could define the rules related to an
organization's group health plan as OLE objects that are accessible to programmers and
applications such as Excel. Repositories and distributed OLE will enable servers and
client applications to use the same objects. This will help the development of enterprise-
wide applications that use common rules.

Industry Support for OLE


At the February Software Development '95 Conference in San Francisco, Microsoft
presented a chart that showed OLE on a growth curve that closely parallels Windows.
According to Microsoft's research, there are currently 400 to 500 OLE applications from
more than 300 independent software vendors. Database companies such as Oracle,
Powersoft, and Gupta Corp. have developed new products or have added OCXs to
existing products. Microsoft supports OLE for Windows and the Macintosh, while Bristol
Technology Inc. offers OLE for Unix platforms. Bristol's Wind/U is a toolset that lets
developers use Microsoft Foundation Classes and the Windows API while developing for
Unix. Scott Wingo, one of the principal engineers of Wind/U, says Bristol plans to
support Microsoft Foundation Classes 3.0 and OLE 2.0 with Wind/U by the summer of
1995.

There is other evidence of strong OLE support in the marketplace. A consortium of


computer-aided design vendors and geographic information system software vendors
recently adopted an OLE extension, OLE for Design and Modeling Applications, that
supports embedding of technical graphics in 2D and 3D documents. Borland's C++
supports OLE 2.0 development with an engine (BOCOLE) that encapsulates OLE, and
provides a complementary class library called the Object Components Framework.
Microsoft's Visual C++ and the Microsoft Foundation Classes are popular for developing
OLE-enabled applications. Visual C++ also includes an OLE Control Development Kit
for developers who want to create OCXs. Visual Basic 3.0 can operate only as an OLE
Automation client; however, Visual Basic 4.0 (scheduled for release when Windows 95
ships) will act as an Automation server and an OCX container.

The Great Object Debate


Comparisons of OLE with other object technologies often evolve into debates about
subclassing, implementation inheritance, and compound documents. Subclassing is the
process of deriving an object from a parent and modifying its behavior. OLE supports
interface inheritance, but not implementation inheritance. One OLE object uses another
object, rather than inheriting the object's implementation. Critics assert that OLE should
support subclassing or inheritance of an object's implementation. Microsoft rebuts that
argument by citing the lack of literature about guaranteed behavior and safe inheritance
when implementing a binary object standard.

Comparisons of OLE and OpenDoc usually focus on the compound document


architecture. OLE proponents raise questions about OpenDoc's ability to operate across
multiple process spaces or the need to distribute parts viewers for compound documents.
Despite the debate, developers who support other interoperable object technologies
recognize OLE's growing popularity. Novell uses OLE as part of the infrastructure for the
Windows version of OpenDoc. OMG members have also been reviewing proposals for
CORBA and OLE interoperability. Regardless of the technical merits of each technology,
it appears that OLE is here to stay.

Ken North is a consultant, author, and software developer. This article includes excerpts
from his book Windows Multi-DBMS Programming, (John Wiley & Sons, 1995). You
can contact Ken via CompuServe at 71301,1306 or at Resource Group Inc., 2604B El
Camino Real, Ste. 351, Carlsbad, CA 92008-1234.
* Borland International, 100 Borland Way, Scotts Valley, CA 95066; 800-233-2444 or
408-461-9000.
* Bristol Technology Inc., 241C Ethan Allen Highway, Ridgefield, CT 06877; 203-438-
6969 or fax 203-438-5013.
* Gupta Corp., 1060 Marsh Rd., Menlo Park, CA 94025; 800-876-3267, 415-321-9500,
or fax 415-321-5471.
* Microsoft Corp., One Microsoft Way, Redmond, WA 98052-6399; 800-426-9400 or
206-882-8080.
* Novell Inc. 122 East 1700 South, Provo, UT 84606-6194; 800-453-1267, 801-429-
7000, or fax 801-429-5155.
* Oracle Corp., Desktop Products Division, 400 Oracle Parkway, Redwood Shores, CA
94065; 800-633-0583 or 415-506-7000.
* Powersoft Corp. (a Sybase company) 561 Virginia Rd., Concord, MA 01742-2732;
800-273-2841, 508-287-1500, or fax 508-369-3997.
* Sylvain Faust Inc., 880 Boul. de la Carriére, Suite 120, Hull, Quebec, Canada J8Y 6T5;
800-567-9127 or 819-778-5045.

TABLE 1. Alphabet soup


CAE Common Applications Environment
CLI call-level interface
CLSID class identifier
COM Component Object Model
CORBA OMG's Common Object Request Broker Architecture
DAO Data Access Objects
DCE OSF's Distributed Computing Environment
DOE SunSoft's Distributed Objects Everywhere
GUID globally unique identifier
IDL Interface Definition Language
IID interface identifier
OCI Oracle Call Interface
OCX OLE custom controls
ODL object description language
OLE object linking and embedding
OMG Object Management Group
OSF Open Software Foundation
RPC remote procedure call
SCM Service Control Manager
SOM IBM's System Object Model
TLB OLE type library
VBA Visual Basic for Applications
VBX Visual Basic extension

NILE: Microsoft's Data Integration Initiative


To improve access to heterogeneous data sources, Microsoft is developing "Nile." That is
the working name for a high-level object interface that will integrate data from relational,
network model, and hierarchical databases, as well as from flat files, spreadsheets, and
other sources. On Microsoft platforms, Nile will act as a layer over COM services and
lower-level technologies such as ODBC, which it will use for relational database access.
Because it will be a high-level specification, theoretically, other vendors could implement
Nile on non-Microsoft platforms using services available in those operating systems.

The need for a stable object interface for data access is apparent. Today, components and
object interfaces lack the consistency to provide "plug-compatible" operation. For
example, Novell's Visual AppBuilder Connection Object (an AppWare Loadable Module)
may not behave like Intersolv Inc.'s MultiLink/VB Connect Control (a Visual Basic
control) or like Sylvain Faust's SQL-Sombrero Connection Object (an OLE custom
control). The purpose of an abstraction or object layer is to provide an interface to a
virtual DBMS that maintains a stable interface, even if the underlying data sources
change. Microsoft hopes that Nile will provide that stable interface, even when the
underlying data does not reside in a DBMS.

As with OLE and ODBC, Microsoft plans to subject Nile to industry review before
publishing it as an open specification. As a published spec, Nile is likely to move to
multiple platforms, as we have seen recently with ODBC and OLE. In February 1995,
Microsoft met with several dozen ISVs to review the first two sections of the draft.
Reviews of the other sections were scheduled to follow soon thereafter. The Nile
specification currently doesn't address the issue of integrating with a repository such as
the one that Microsoft is developing with Texas Instruments. Also, Microsoft's initial
review of Nile did not cover transaction processing, but later reviews will cover that
topic.

--Ken North
FIGURE 1.

--OLE is a layered architecture where OLE services such as In-Place Activation and
Linking use lower level services such as Structured Storage. OLE builds on an
infrastructure of services for Component Objects known as the Component Object
Model.
FIGURE 2.

--Windows Objects can expose multiple interfaces such as Interface A and Interface B.
An object's interface (its methods or member functions) is accessible through virtual
tables that contain a pointer to each member function. The data that the object
encapsulates is not exposed to an object user.

Table 2. Applications use OLE 2.0 services by accessing these


interfaces
Component Object Interfaces
Interface Purpose
IClassFactory The interface through which server and container applications
create instances of an object class.
IEnumX Iterates through an item list.
IExternalConnection Implemented by DLL object applications to provide an orderly
shutdown of object links.
IMalloc Used by OLE to allocate and free memory.
IMarshal Provides process space transparency of interface pointers for
lightweight remote procedure calls.
IStdMarshalInfo Returns the class ID (CLSID) of the object handler that is to
marshal data to and from the object.
IUnknown The base interface that is common to all OLE applications.
Compound Document Interfaces
Interface Purpose
IAdviseSink Receives asynchronous notifications from embedded or linked
objects.
IAdviseSink2 Receives notifications of link source changes.
IEnumOLEVERB Enumerates the verbs available for an object.
IOleAdviseHolder Keeps track of IOleObject::Advise calls and sends notification
to registered links.
IOleClientSite Provides services to an OLE object from its container.
IOleContainer Enumerates objects in a container.
IOleItemContainer Used for binding item Monikers.
IOleObject Provides a variety of member functions to manage OLE objects
such as getting Monikers, CLSIDs, clipboard data, and so on.
IRunnableObject Indicates to object handlers and DLL object applications when
to run or become a contained object.
Data Transfer/Caching Interfaces
Interface Purpose
IDataAdviseHolder Keeps track of IDataObject::DAdvise calls and sends change
notifications to object handlers and servers.
IDataObject Supports format enumeration, data retrieval, transfers to and
from objects, and notification of object changes.
IEnumFORMATETC Enumerates object data formats.
IEnumSTATDATA Enumerates an object's advisory connections.
IOleCache Controls the data cached inside an embedded object and
determines the container's access to data when the object's
server is unavailable.
IOleCache2 Extends IOleCache to permit clients to update each of the
maintained caches.
IOleCacheControl Used by object handlers and DLL object applications to
associate the cache part of the handler with the running object's
IDataObject implementation.
IViewObject Provides an object image or picture using a caller-specified
device context.
IViewObject2 An extension to IViewObject to provide containers and object
handlers with the view extents of an object.
Linking Interfaces
Interface Purpose
IBindCtx The bind context is used internally for purposes such as
managing the list of bound objects.
IEnumMoniker Enumerates the Monikers of which an object is a part.
IMoniker Accesses and controls Monikers, and provides object binding.
IOleLink Provides an interface for updating the Moniker inside a linked
object and manipulating its update options.
IParseDisplayName Parses an object Moniker's display name.
IRunningObjectTable Provides an interface to the global inventory of currently
running objects .
Structured Storage
Interface Purpose
IEnumSTATSTG Used by OLE to enumerate IStorage objects.
ILockBytes Saves compound document objects to disk-based compound
files, byte arrays, and custom storage such as relational
databases.
IPersist Obtains an object's CLSID. Parent of IPersistStorage,
IPersistStream, IPersistFile.
IPersistFile Used to load documents that reside in a file.
IPersistStorage Provides methods that containers can call to have a server load
and save data.
IPersistStream Used to save and reload objects stored in a serial stream.
IRootStorage Switches the underlying disk file where objects are saved.
IStorage Instantiates a directory-like collection of storage and stream
objects.
IStream Manipulates the underlying bytes of data that comprise an
IStorage object.
Drag and Drop
Interface Purpose
IDropSource Provides feedback and status information (for example, key
state) to applications implementing drag-and-drop.
IDropTarget Implemented to communicate status (for example, key state and
mouse location) with the drop source by applications that
support dropped data.
In-Place Activation
Interface Purpose
IOleWindow Contains methods that obtain the handle of the in-place
window.
IOleInPlaceObject Activates and deactivates an in-place object.
IOleInPlaceActiveObject Provides communication between the in-place object and the
frame and document windows.
IOleInPlaceUIWindow Manipulates the container's document window.
IOleInPlaceFrame Controls the application's top-level frame window.
IOleInPlaceSite Provides an interface to the object's in-place client site.
Concurrency Management
Interface Purpose
IMessageFilter Filters Windows messages while waiting for responses from
synchronous calls.
Programmable Controls
Interface Purpose
IOleControl Used by a control to communicate with its container.
IOleControlSite Used on a container's site objects to communicate with a
control.
IConnectionPoint Used to enumerate connection points for event dispatching.
Container
IConnectionPoint Specifies a dispatch point for an event.
Source: Ken North's Windows Multi-DBMS Programming (John Wiley & Sons Inc.,
1995).
Subscribe to DBMS and Internet Systems -- It's free for qualified readers in the United
States
June 1995 Table of Contents | Other Contents | Article Index | Search | Site Index | Home
DBMS and Internet Systems (http://www.dbmsmag.com)
Copyright © 1995 Miller Freeman, Inc. ALL RIGHTS RESERVED
Redistribution without permission is prohibited.

OLE Component Object Model


0. Intended Use

OLE (and its Component Object Model) provide a basis for application interoperation in
the Microsoft Windows environment.

1. Basic Concepts

Microsoft's OLE provides an application integration framework for Microsoft Windows.


OLE defines the Component Object Model, which specifies a programming-language-
independent binary standard for object implementations (i.e., it specifies what the
implementation of the objects has to look like). Any object conforming to this standard is
a legitimate Windows Object, no matter what language is used to implement it. The
programming model is synchronous, based on a "Lightweight Remote Procedure Call"
(lightweight because, at least at the moment, the calls are not really remote; they are all
made on one machine).

In the Component Object Model, the concept of interface assumes great importance. An
interface is "a set of semantically related functions implemented on an object" [Bro94a].
The Component Object Model uses the word "interface" by itself to refer to the definition
(signatures) of those functions. An implementation of an interface is an array of pointers
to functions. Any code that has a pointer to that array can call the functions in that
interface. A Windows Object implements one or more interfaces, i.e., provides pointers to
function tables for each supported interface.

Users of objects always obtain and act through pointers to object interfaces; users never
obtain pointers to an entire object. For example, when the user of some object first
obtains a pointer to the object, the user actually gets a pointer to one of the object's
interfaces. This pointer allows the user to call only the functions in that one interface's
function table. Through this pointer, the user has no access to any state of the object, nor
does the user have any direct access to functions in other interfaces.

OLE defines a standard function, called QueryInterface, through which the user of one
interface of an object can obtain a pointer to another interface of the same object.
QueryInterface is part of an interface called IUnknown, which defines a group of
fundamental functions that all Windows Objects support (thus IUnknown is supported by
all objects). All other interfaces in OLE are derived from IUnknown, so all interfaces
contain the QueryInterface function. This insures that navigation is always possible
between the interfaces of a given object.

Using QueryInterface, the user of an object can discover the capabilities of that object at
run-time by asking for pointers to specific interfaces. This enables an object to implement
as many interfaces as it wants. Because all Windows Objects implement at least
IUnknown, there is always some basic way for a user to communicate with any object.

The function table that implements an interface is designed to have a layout that is
identical to the one generated by many C++ compilers. This layout allows a single
indirection (->) on the pointer to call an interface function. However, while this makes
the use of C++ to program OLE convenient, this is not a requirement. An object
implementation is only required to provide separate function tables for each supported
interface. How these tables are created can differ, depending on the particular language
used. Because neither use or implementation of a Windows Object is dependent on the
programming language used, the Component Object Model is referred to as a binary
standard. This provides for language independence without involving the definition of a
separate language (e.g., an Interface Definition Language).

2. Objects

The Component Object Model specifies a programming-language-independent binary


standard for object implementations (i.e., it specifies what the implementation of the
objects has to look like). Any object conforming to this standard is a legitimate Windows
Object, no matter what language is used to implement it.

Users of objects always obtain and act through pointers to object interfaces. An
implementation of an interface is an array of pointers to functions (the functions
themselves are implemented by the object). Any code that has a pointer to that array can
call the functions in that interface. A Windows Object implements one or more interfaces,
i.e., provides pointers to function tables for each supported interface. Users never obtain
pointers to an entire object. For example, when the user of some object first obtains a
pointer to the object, the user actually gets a pointer to one of the object's interfaces. This
pointer allows the user to call only the functions in that one interface's function table.
Through this pointer, the user has no access to any state of the object, nor does the user
have any direct access to functions in other interfaces.

A Windows Object is any object, in whatever form, that supports at least one predefined
interface, called IUnknown. As part of the IUnknown interface, OLE defines a standard
function, called QueryInterface, through which the user of one interface of an object can
obtain a pointer to another interface of the same object. QueryInterface takes as input a
pointer to an interface identifier (IID) for the desired interface, and either returns an error
(and a NULL pointer), meaning the object does not support the interface, or a valid
pointer to the new interface. [See entry under 6. Identity, Equality, Copy for a
discussion of IIDs.] All other interfaces in OLE are derived from IUnknown, so all
interfaces contain the QueryInterface function (the other two functions of IUnknown are
AddRef and Release). This insures that navigation is always possible between the
interfaces of a given object.

A Windows Object must be able to provide a separate function table for each interface it
supports. The implementation of the IUnknown functions in each supported interface
must be "aware" of the entire object, because they must be able to access all other
interfaces in the object and must be able to affect the object's reference count.

The implementation in the component object library (COMPOBJ.DLL) provides a small


number of fundamental API functions that permit creation of what is called a Component
Object, a special type of Windows Object identified with a unique class identifier that
associates an object with a particular DLL or EXE in the file system. A Windows Object
does not always need to be structured as a Component Object such that the API functions
in COMPOBJ.DLL can create it. Use of such API functions is merely one way through
which an initial pointer to an object can be obtained.

Unlike C++, where objects are defined using class definitions which generate user-
defined types, Windows Objects are defined in terms of the interfaces they support. Since
all objects support at least one interface (IUnknown), all Windows Objects are at least of
type IUnknown, and can be treated as being of another type by using a different interface.
Because of this mechanism, there is no single user-defined type associated with a
Windows Object class, as there is with a C++ class. In fact, there is no specific way to
identify a specific object. This is because object references (pointers) in Windows Objects
are not references to the object itself, as in C++, but rather are pointers to one of the
object's interfaces. Given a pointer to an interface, the user can access only functions
contained in that interface. The user can never have a pointer to the whole object
(because there is no direct user-visible concept of "whole object"), so there is no direct
access to state, and no concept of "friend" as in C++. Through the IUnknown interface, a
user can obtain pointers to other interfaces that the object also supports, but this means
obtaining a different pointer that refers (indirectly) to the same object. Each pointer to an
interface points to a function table associated with the object, and each table contains
only functions for a specific interface. Because a pointer to a Windows Object always
points to a function table, such a pointer can also be used from within programs written in
languages other than C++, such as C or assembly code.

The list of interfaces that an object of a specific class supports is constant only within a
specific object's lifetime, and can vary between different instances of objects of the same
class. It cannot be assumed that if Object 1 of class X supports a particular set of
interfaces, Object 2 of class X does as well. (Note that the "class" denotes the application
providing the object's implementation, not the set of interfaces (type) supported). It also
cannot be assumed that if objects of class X once supported interface Y, they always will,
because the object might change later. This provides justification for the QueryInterface
mechanism of dynamically finding out about interfaces. It is always possible to find out
about other interfaces the object supports; thus, if an object is acquired as an instance of a
"superclass", it is possible to find out what specific "subclass" it is by examining the
other interfaces at runtime. There is no requirement to always treat an object as an
instance of the type (interface) through which it was originally acquired.

2.1 operations

Operations resemble standard C++ functions, and are defined as part of interface
definitions. Operations are always invoked indirectly, through interfaces, as described in
the entry under 2. Objects.

2.2 requests
Requests resemble calls to C++ functions. However, functions are always called
indirectly, through interfaces, as described in the entry under 2. Objects.

2.3 messages

2.4 specification of behavioral semantics

2.5 methods

Methods in the Component Object Model are essentially equivalent to C++ member
functions.

2.6 state

State in the Component Object Model consists of a set of stored values essentially
equivalent to C++ data members.

2.7 object lifetime

In C++, objects are constructed using the class's constructor function. There are a number
of ways to create a Windows Object, but a common way is to use a class factory object. A
class factory object represents a specific class identifier, is obtained by a specific OLE
function, and supports an interface named IClassFactory. The IClassFactory interface
contains a function named CreateInstance, to which is passed an identifier of the desired
interface to that object. The expression IClassFactory::CreateInstance is the logical
equivalent of C++'s new.

In C++, an object is destroyed by calling the delete operator on an object pointer (which
ultimately causes the object's destructor function to be called). The corresponding
function that frees a Windows Object (and essentially calls its destructor) is a function
called Release. This function is part of the IUnknown interface, and is thus present in
every interface. However, calling Release does not necessarily destroy the object.
Internally, the object maintains a count of how many references exist to any of its
interfaces. Creating an interface pointer increments the reference count, whereas Release
decrements it. When the count is reduced to zero, the object frees itself, calling its own
destructor.

2.8 behavior/state grouping

The Component Object Model implements a classical object model.

2.9 communication model

The programming model for Windows Objects is synchronous, based on a "Lightweight


Remote Procedure Call" (lightweight because, at least at the moment, the calls are not
really remote; they are all made on one machine). Further development will allow the
lightweight RPC to be replaced by genuine (distributed) RPC.

2.10 events

OLE handles event notifications through an object called an advise sink--that is, an object
that absorbs notifications from a source. The advise sink not only handles notifications
for data changes, but it also is generally used to detect changes in another compound
document object, such as when it is saved, closed, or renamed. Specifically, an object that
is interested in being notified about changes to a specific data object implements an
object with an IAdviseSink interface, and passes a pointer to this interface to the data
object using the DAdvise function of the IDataObject interface (see entry under 9.2
attributes). Whenever its data changes, the data object calls the OnDataChange function
in the IAdviseSink interface it has been passed. (The IAdviseSink interface also contains
other notification functions, such as OnRename, OnSave, etc.)

3. Binding

"Binding", meaning the choice of a method to be executed in response to a request, is


handled by directly calling the object function identified in the request. OLE does not
support implementation inheritance (see entry under 8. Inheritance and Delegation), so
there is no dispatching by searching a class hierarchy. The term "binding" as used in the
OLE context has a somewhat different meaning, relating to the use of a type of object
called a moniker in object linking. See entry under 9.6 other.

4. Polymorphism

The Component Object Model is polymorphic in the sense that what appears to be the
same request can be sent to any interface supporting the requested operation; the
interfaces need not refer to objects of the same class. However, unlike models supporting
a "conventional" subtyping mechanism for objects having a single interface, the
interfaces in Windows Objects remain distinct. Through a pointer to a Y interface, the
object cannot be treated as an X, even if the object also has an X interface; instead, the
user must explicitly get a pointer to the X interface.

See also entry under 8. Inheritance and Delegation.

5. Encapsulation

Objects may only be accessed through the operations defined in one of the object's
interfaces. Moreover, given a reference to one interface, only the operations in that
interface may be used. Operations in another interface may only be used after first
obtaining a reference to that interface.

6. Identity, Equality, Copy


Object references (pointers) in Windows Objects are not references to the object itself, as
in C++, but rather are pointers to one of the object's interfaces. In fact, there is no specific
way to identify a specific object (i.e., it is only possible to obtain references to interfaces,
not whole objects). Given a pointer to an interface, the user can access only member
functions contained in that interface. The user can never have a pointer to the whole
object (because there is no direct user-visible concept of "whole object"), so there is no
direct access to data members, and no concept of "friend" as in C++. Through the
IUnknown interface, a user can obtain pointers to other interfaces that the object also
supports, but this means obtaining a different pointer that refers (indirectly) to the same
object. Each pointer to an interface points to a function table in the object, and each table
contains only member functions for a specific interface. Because a pointer to a Windows
Object always points to a function table, such a pointer can also be used from within
programs written in languages other than C++, such as C or assembly code.

Every interface is associated with an interface identifier, or IID. An IID is a special case
of a universally unique identifier, or UUID. The universally unique identifier is also
known as the globally unique identifier, or GUID. GUIDs are 128-bit values created with
a DEFINE_GUID macro. Every interface and object class uses a GUID for identification.
As described in the OLE SDK, Microsoft will allocate one or more sets of 256 GUIDs for
a developer's exclusive use on request. Alternatively, a user with a network card can run a
tool UUIDGEN.EXE that will provide a set of 256 GUIDs based on the time of day, the
date, and a unique number contained in the network card [Bro94a].

OLE defines IIDs for every standard interface along with class identifiers (CLSID) for
every standard object class. When a function is called that asks for an IID or CLSID,
what is actually passed is a reference to an instance of the GUID structure that exists in
the process space (using the reference types REFIID or REFCLSID). To compare two
GUID, IID, or CLSID values for equality, the functions IsEqualGUID, IsEqualIID, and
IsEqualCLSID are used. In C++, an overloaded "==" operator can be used.

The QueryInterface function must always behave according to specific rules which,
among other things, implement an indirect concept of object identity. First, any call to
QueryInterface through any interface on a given object asking for a pointer to the
IUnknown interface always returns an identical pointer value. This means that, given two
arbitrary interface pointers, it is possible to determine whether they belong to the same
object by asking each for an IUnknown pointer and comparing the returned pointer
values. If they match, both interface pointers refer to the same object. Second, after an
object is created, the interfaces it supports are static. If QueryInterface succeeded for a
particular interface at one point in the object's lifetime, an identical call to QueryInterface
at a later time will also work. (This does not mean that the exact pointer values returned
will be identical, just that the interface is always available). The static set of available
interfaces applies to a specific object, not an object class. That is, two objects of the same
class might not both support the same interfaces, but during the lifetime of each, the
interfaces they each support will remain static. Finally, as along as an object is in
existence, all interface pointers obtained on that object must remain valid, even if the
Release function has been called through those pointers.
7. Types and Classes

Unlike C++, where objects are defined using class definitions which generate user-
defined types, Windows Objects are defined in terms of the interfaces they support. Since
all objects support at least one interface (IUnknown), all Windows Objects are at least of
type IUnknown, and can be treated as being of another type by using a different interface.
Because of this mechanism, there is no single user-defined type associated with a
Windows Object class, as there is with a C++ class (examples of standard Windows
Objects include such things as windows, dialogs, messages, controls, and GDI objects,
such as pens, brushes, fonts, and bitmaps).

A Windows Object class is identified as such only through a class ID (a structure called
CLSID) that associates an object with a particular DLL or EXE in the file system (e.g.,
the application that implements the object). The class ID is stored in a registration
database, along with information that defines where the object "lives" and characteristics
that a potential user may wish to know without having to actually instantiate the object.
The registration database is stored in REG.DAT in the Windows directory. Under
Windows Objects, a class object represents a specific class ID, is obtained by a specific
OLE API, and supports an interface called IClassFactory. Every component object class
(but not all types of Windows Objects) must have a unique CLSID associated with it in
the registration database (i.e., Windows Objects can exist that do not have defined classes
in this sense).

See also entry under 2. Objects.

8. Inheritance and Delegation

Windows Objects and the classes they identify through class identifiers have no notion of
implementation inheritance. One Windows Object does not inherit the implementation of
another Windows Object. Instead, reuse of objects is supported through the containment
and aggregation mechanisms. In the Component Object Model, inheritance is simply
considered as a language-specific tool (e.g., in C++) that may be useful for implementing
classes and defining interfaces in that language. The reason given for not supporting
inheritance is that systems built on it must ship all their source code in order to be useful
[Bro94a]. For example, inheritance cannot be used to inherit from objects used in the
operating system itself, for which source code is not available.

In the Component Object Model, both the containment and aggregation mechanisms
work by using the implementation of another object. However, the object being used
remains entirely self-contained and operates on its own instance of data. The containing
object also works on its own data, and calls the other object as necessary to perform
specific functions for which it can be passed the data on which to operate.

To implement what corresponds to a subclass Y of a class X using containment, class Y


completely contains an X object and implements its own version of the X interface which
it exports to clients. This makes Y a simple user of X, and X need not know about its use
within Y. This is useful when Y needs to override some aspect of X's behavior. Since all
external calls go to the Y implementation first, Y can either override selected behavior or
pass the calls directly through to X.

To implement what corresponds to a subclass Y of a class X using aggregation, class Y


directly exposes X's interface. This requires that X "know" that its interface is exposed
for something other than itself, such that the QueryInterface, AddRef, and Release
functions behave as a user expects (e.g., X's QueryInterface function must be capable of
returning references to interfaces implemented by Y which, as a part of an X interface, it
did not originally know about; OLE provides a mechanism for dealing with this when an
aggregate is created).

Windows Objects do support a specific case of interface inheritance, in that all other
interfaces derive from IUnknown, as described in the entry under 2. Objects. Generally,
however, unlike models supporting a "conventional" subtyping mechanism for objects
having a single interface, the interfaces in Windows Objects remain distinct. Through a
pointer to a Y interface, the object cannot be treated as an X, even if the object also has an
X interface; instead, the user must explicitly get a pointer to the X interface.

C++ multiple inheritance is a convenient way to provide multiple function tables for each
interface, since the compiler generates them automatically. Because each implementation
of a C++ member function is already part of the object class, each automatically has
access to everything in the object. [Bro94a] also discusses a more general approach to
constructing objects, for use by programmers in C and other languages that do not
provide built-in inheritance. In this approach, a C++ object class corresponding to the
Windows Object class inherits from IUnknown, and implements these functions to
control the object as a whole. Each interface supported by the object is then implemented
in a separate C++ class that singly inherits from the interface it is implementing. These
"interface implementations" are instantiated with the object, and live as long as the object
lives. The IUnknown members of these interface implementations always delegate to
some other IUnknown implementation, which in most cases is the overall object's
IUnknown. Each interface implementation also holds a "back pointer" to the object in
which the implementations are contained so that they are able to access information
centrally stored in the object. In C++, this generally requires that each interface
implementation class be a friend of the object class.

9. Noteworthy Objects

9.1 relationships

9.2 attributes

As described in the entry under 2. Objects, Windows Objects are accessed through
interfaces consisting of sets of functions. Windows Objects that include "data" to be made
available to users can be defined with a special interface for accessing that data.
Specifically, a data object is a Windows Object that provides a standard data transfer
interface called IDataObject. IDataObject includes, among other things, functions for
getting and setting data (GetData, SetData, GetDataHere), for querying the ability of the
object to provide data in specific formats (QueryGetData), and for notifying clients of the
data when the data changes in various ways(DAdvise, DUnadvise).

9.3 literals

9.4 containment

See entry under 8. Inheritance and Delegation.

9.5 aggregates

See entry under 8. Inheritance and Delegation.

9.6 other

The use of the object linking supported by OLE can introduce problems in maintaining
referential integrity. Specifically, since the data referenced by linked objects lives in a
separate file on the file system, links are easily broken when the end user manually
changes the location of that file...To solve most of the link breakage problems as well as
to provide for arbitrarily deep object nestings, OLE uses a type of object called a
moniker.

A simple moniker contains some reference to a linked object and contains code that
knows how to "bind" to that linked object. Binding is the process of launching the
application that handles the class of the linked object, asking the application to load a file
in which the object lives, then asking the application to resolve the name of the object
down to an object pointer.

A file moniker is used to store either an absolute or relative pathname. A linked object
maintains an absolute moniker and a relative moniker. If it fails to locate the file with the
absolute, it tries the relative moniker. Complex object references are described using
composite monikers that are sequences of any other simple or composite monikers. Most
links can be expressed in a composite of one file moniker and one item moniker, i.e., a
link to an embedded object (the item) in a container document (the file). The item name is
only meaningful to the application that created it. That application will be asked later to
return a pointer to the object identified by the item name...An example of a composite
moniker might be one that contains a file (a spreadsheet) and an item moniker (a cell
reference).

Editor's note: For the benefit of you word origin fans, a "moniker" (or "monica") was
originally a nickname taken by a hobo (one of Jack London's was "Skysail Jack"). Later
the term became used colloquially to mean any form of name for a person, including an
alias, or his or her real name.
10. Extensibility

10.1 Dynamic

10.2 Metaclasses/Metaobject Protocol

10.3 Introspection

11. Object Languages

The Component Object Model specifies a programming-language-independent binary


standard for object implementations (i.e., it specifies what the implementation of the
objects has to look like). Any object conforming to this standard is a legitimate Windows
Object, no matter what language is used to implement it.

12. Semantics of Base Classes (+ type constructors)

13. Background and References

[Bro94a] K. Brockschmidt, Inside OLE 2, Microsoft Press, Redmond, 1994.

[Bro94b] K. Brockschmidt, "OLE 2.0 Part I: Windows Objects and the Component
Object Model", Microsoft Systems Journal, Aug. 1993.

[Bro94c] K. Brockschmidt, "OLE 2.0 Part II: Implementing a Simple Windows Object
Using Either C or C++", Microsoft Systems Journal, Sept. 1993.

You might also like