Component Object Model (COM)

To facilitate understanding of Microsoft business service technologies, we'll examine COM, which is the foundation of many of the technologies we will discuss in this book. This chapter is not intended as an exhaustive discussion of COM, but we will briefly review some of the basic concepts developers should know before they begin their application architectures. Recommendations for further reading are listed in the bibliography.

NOTE
Another leading object model and implementation in the computer industry is Common Object Request Broker Architecture (CORBA). For CORBA-specific information, we recommend Alan Pope's The CORBA Reference Guide (Addison-Wesley, 1998).

Why COM?

The fundamental goal of COM is to enable developers to create applications assembled from pre-built parts, or components. Components are the physical binary implementations of the business object. For example, an order-entry application could use a data-entry grid component to simplify entering ordered items into the application. Another component could look up a customer's city and state by entering the customer's ZIP code or postal code. Yet another component could calculate the order's sales tax. To make such a component-based application a reality, several technical challenges must be met.

The first technical challenge is how to locate the component on a computer or network and, once located, to execute the component. The locating of components is also referenced as a directory service for the components. If no such standards exist, overhead costs of learning to use components is very high. Additionally, the costs and inconsistencies of coding logic to locate additional components and create objects would present significant barriers to reusing pre-built components. In conjunction with these standard mechanisms used for internally created components, applications must also be available to identify and execute vendor-created components.

The second technical challenge is to provide standards for the application to interact with the components. Again, if no standards exist, the overhead cost of learning how to use such objects creates a barrier to reusing code. Ideally, a standard mechanism for object interaction would not distinguish the location of any given object, whether the object exists within its own application's process, within another process on the application's host computer, or within a different process on another computer altogether. Inter-process and remote communications usually require tremendously complex coding within an application. Providing standards for object interaction will allow application and component developers to spend less time creating such complex code.

The third technical challenge is executing true language independence, a complex challenge for any object model. Every element involved in a particular object—memory allocation, method names, parameter types, calling conventions, and so on—must be defined in such a way that the object can be created in one programming language and used by another. Application developers should not need to spend valuable time or energy worrying which programming languages or tools are used to create a desired component. Without wide support from development languages and tools, many different models will fragment the component market. This fragmentation raises the costs of identifying, purchasing, and developing components that are operable only in certain environments.

Finally, an ongoing challenge is in maintaining the potential to create newer versions of applications and components. Applications developed at different times can implement identical components, and thus cause usage conflicts, when running on a given computer. As developers continue to upgrade components to maintain function in the ever-changing world of computer technology, version compatibility must be maintained. These ongoing upgrades can involve fixing problems in existing component features, adding new functionality to existing components themselves, or creating new components altogether.

The following subsections review elements and terminology of COM that are commonly used in application development.

Objects

"Object" is one of the most overloaded terms in programming. As with most object-oriented models, COM objects are run-time instances of a particular class, with the class representing a real-world entity. We'll define the exact meaning of classes later in this chapter; conceptually, however, classes are types of objects. For example, based on their characteristics, classes could define a Customer, Order, or SalesTaxCalculator. Thus, each Customer object would represent a specific instance of a real-world customer; each Order object a specific instance of an order; and so on.

An object usually contains an identity, a state, and a type of behavior. Identity is the unique name or label distinguishing one object from any other. State represents data associated with a particular object. Behavior is a set of methods called to query or manipulate an object's state.

To help clarify these concepts, let's examine C++ objects, which are run-time instances of C++ classes. A C++ class defines member variables and methods that apply only to objects of this particular class. Upon a particular object's creation, a contiguous block of memory becomes allocated for member variables; the allocated memory's address in effect becomes the object's identity and the memory block's contents become the object's state. Located elsewhere in memory, method implementation code defines the object's behavior.

Most language-based object models are similar to that of C++, but the COM object model is somewhat different. Recall that two challenges faced by COM are language- and location-independence. When developers begin to examine inter-process and remote communications, memory addresses are not sufficient to identify objects. In addition, compatibility among all programming languages and tools regarding memory layout for object member variables is nearly impossible.

Accounting for these potential complications, COM approaches objects in a different manner than C++. In COM, the notion of an object's public interface and its implementation are completely separate. Applications can interact with COM objects only through the object's public interfaces using a COM-defined interface pointer. Since all interactions must go through the interface pointer, COM ignores an object's state location and memory inner workings. Additionally, since an interface pointer is the only means through which an application references a given object, the object's identity must relate to that pointer.

Interfaces

Understanding interfaces is essential to understanding COM. A COM interface is a collection of logically related operations that define a particular behavior. When developers define an interface, they provide specifications only for a set of operations, but not for implementations. Interface definitions represent a con-tract between a caller and an implementer: if a component implements a particular interface, the caller can expect the component to obey the interface specification. Such a specification includes a strict definition of interface method syntax, as well as a definition of interface semantics.

To be defined as a COM interface, such an interface must satisfy the following requirements:

  • A unique identifier must identify the interface.
  • The interface must ultimately derive from the special interface IUnknown.
  • Once published, the interface must be immutable. In other words, the interface can't be changed.

COM Identifiers

Unique COM identifiers are needed to locate components, and also to reference each interface. Providing a unique identifier to cite each interface could involve using a string identifier, which could cause several problems.

The most critical issue raised by performing this action is the difficulty of guaranteeing the selection of a truly unique identifier. Even when a naming convention is imposed, there is a possibility that another developer elsewhere in the world will use the same identifier for a different purpose. To guarantee uniqueness, prefixes could be issued from a central authority—for example, one prefix per company. Each company would, in turn, need a central registry of names to prevent any duplicates within the company. To impose such a method on string identifiers seems much too complicated.

Instead of using string identifiers, COM implements globally unique identifiers (GUIDs, pronounced "goo-ids" or "gwids"), which are 128-bit system-generated integers that uniquely identify components. The algorithm used to generate GUIDs is statistically guaranteed to generate unique identification numbers.

NOTE
According to the COM specification, GUIDs can be generated at the rate of 10,000,000 per second per computer for the next 3,240 years without risk of duplication.

GUIDs can be generated using a tool such as GUIDGEN, which accompanies the Microsoft Platform Software Development Kit (SDK). GUIDGEN calls the system application programming interface (API) function CoCreateGuid to generate the GUID, then provides several output options. For example, the following GUID was generated using the static const output option, and is suitable for inclusion in a C++ source file:

// {45D3F4B0-DB76-11d1-AA06-0040052510F1} static const GUID GUID_Sample = { 0x45d3f4b0, 0xdb76, 0x11d1,     { 0xaa, 0x6, 0x0, 0x40, 0x5, 0x25, 0x10, 0xf1 } }; 

The first line in the code example shown above is a comment showing how a GUID appears in string form; GUIDs are normally presented to users in this string form. The second and third lines in the code example above define the GUID as a constant that can be used in C++ code.

NOTE
Most development tools automate the process of creating skeleton COM components. Such tools also generate appropriate GUIDs for developers in a format that the skeleton COM code can recognize.

Every interface is identified by a GUID. Whenever we need to uniquely identify an interface to COM, we use its GUID, which we call an interface identification (IID). The IID can be complex numerical figures such as {45D3F4B0-DB76-11d1-AA06-0040052510F1} or {45D3F4B1-DB76-11d1-AA06-0040052510F1}.

To simplify IID standards for use in source code, every interface should also have a string name. Conventionally, these string names usually begin with the letter "I"—for example, IComputeSalesTax. String names aren't guaranteed to be unique, but it's unlikely that two different interfaces with an identical string name would be used in one source code file.

Defining Interfaces

Concern may arise over how to define interfaces so that component developers know how to implement them and application developers know how to use them. COM does not rigidly distinguish interface definitions, as long as both component and application developers can agree on the definitions. In practice, COM interfaces are usually defined using the Interface Definition Language (IDL).

Similar to C++, IDL is a language that describes the exact syntax of an interface. Interface definitions begin with an interface keyword; interface attributes are contained in square brackets preceding the interface keyword. An object attribute indicates that COM IDL extensions should be used, and a UUID attribute (see the following note) specifies the IID for the interface being defined.

NOTE
The attribute is named UUID because COM IDL is based on the Open Software Foundation's Distributed Computing Environment (DCE) IDL, which uses the term "universally unique identifier" (UUID) instead of GUID.

Not all development languages will support all data types that can be specified using IDL. Most interfaces use a fairly restricted set of data types. We discuss this limitation in more detail later in this chapter. COM methods can have any number of parameters and arbitrarily complex data types. Beside simple types such as a name and data type, developers can specify additional attributes for each IDL parameter. These attributes provide clues about how data should be copied from one place to another. IDL is a convenient text format for documenting the interface syntax. Once the IDL code is written, it can be compiled using the Microsoft IDL (MIDL) compiler to generate the following equivalent interface definition representations, which are useful to development tools:

  • Header file Header files define the IDL types that can be used to declare interface pointer variables, or to derive an implementation class for an interface. Header files can be included in a C or C++ program.
  • Type library Type libraries are binary representations of IDL code. Development languages, such as Microsoft Visual Basic, read the type library to determine the syntax of the interfaces described in the type library.
  • Source code for a proxy/stub DLL This source code implements proxy/stub DLLs used for inter-process and remote communication.

While not difficult to use, IDL is still new to many developers; thus most development languages and tools offer assistance. Some systems, such as Visual Basic, completely hide IDL by allowing developers to define interfaces directly in the system's syntax while the system itself generates a type library for the interface. Other tools generate the IDL file automatically, usually by providing a wizard or similar automated guide to help developers define an interface and its methods; after the interface and its methods have been defined, the tools generate the correct IDL syntax.

Defining interfaces using IDL is a preliminary step toward language independence, but not a complete solution. Given an IDL file, or an equivalent header file or type library, an interface or client application implementation can be coded, as long as it is done in any language recognizing the interface's types used. To ensure communication between the implementation developed and the client computer, both client and implementation must agree on what an interface pointer would represent, and how that pointer intermediates method calls.

COM as a Binary Standard

COM, as a binary standard for object interaction, addresses many component challenges. COM is a binary standard because:

  • It is part specification-oriented.
  • It is part implementation-oriented.

COM is part specification-based in that it defines objects in a language- and location-independent manner, as well as how to locate and identify components, and also create objects. A COM component's interface can be recognized with the IDL, or equivalent header file or type library.

COM is also part implementation-based in that it provides system services that locate components and load them into memory. Additionally, COM can perform inter-process and remote communications as well as other needed tasks. COM defines the exact binary representation of an interface. Any programming language or tool that supports COM must create object interfaces that correspond to this standard binary representation.

The client's interface pointer is actually a pointer to a table of more pointers. The table of pointers is called the vtable. Each pointer in the vtable points to the binary code for an interface method in exactly the same manner as in a C++ virtual function table.

A specific pointer to a vtable is appropriately called a vtable pointer. Each COM object contains a vtable pointer for each interface it supports. A client requesting an interface pointer to an object obtains a pointer to an appropriate vtable pointer, not the vtable pointer itself. The vtable pointer needs an additional pointer to support an interface because the component needs a way to identify the object on which it should be working.

When a COM object is created, a single block of memory is usually allocated for both vtable pointers as well as any internal data members that the object needs. The component recognizes the relationship between the locations of both the vtable pointer and the object's entire memory block; thus, this component can identify its appropriate object. By using the interface pointer, COM further specifies that the first parameter passed to each method call is a pointer to the particular object mentioned.

Fortunately, most COM-supportive programming languages and tools automatically map interface pointers and vtables to equivalent concepts in the languages themselves. For example, C++ interfaces are equivalent to abstract base classes. These interfaces can be implemented by deriving a particular class from the abstract base class. Calling COM methods through an interface pointer is exactly like calling C++ methods through an object pointer. As another example, Visual Basic interfaces are almost completely hidden within the Visual Basic language itself. An interface can be implemented by using the implements keyword and thereby implementing the interface's methods. To use a COM object, declare an interface type object variable, create the object, and make function calls as normal.

Combined with a common interpretation of interface definitions, the binary standard for interfaces provides language independence as well as the potential for complete location independence. As mentioned, it's ideal to make in-process, inter-process, and remote calls identical on the client computer. Within a single process, the interface pointer can direct itself to the original vtable pointer and call methods directly. Although such a technique probably wouldn't work across different processes or computers, the interface pointer could be redirected to point to a proxy vtable pointer. The client-side proxy would presumably recognize methods in which to make inter-process or remote calls to an equivalent server-side object, or stub; in turn, that particular object would make in-process calls to the original object. To clients and components, method calls would appear identical.

The IUnknown Interface

A vtable contains three methods—QueryInterface, AddRef, and Release—not defined by the developer for a COM component interface. The IUnknown interface provides these three methods, and also defines fundamental behavior for COM interfaces. Clients can rely on this fundamental behavior because all COM interfaces derive from IUnknown. IUnknown helps resolve the technical challenge of providing a standard means to interact with objects, and additionally provides three important features: interface navigation, interface versioning, and object lifetime management.

Interface Navigation

Interface navigation is provided by the QueryInterface method. As mentioned previously, COM objects can support more than one interface. For example, if developers have one interface pointer to an object and desire another, they can request it from the object using the QueryInterface method. Because all interfaces derive from IUnknown, every interface conveniently supports QueryInterface.

To use QueryInterface, a client chooses an IID it wants to pass to an object. If it supports that interface, the object passes back the interface pointer. If not, the object returns an error. QueryInterface is an extraordinarily powerful mechanism that allows independently created clients and components to negotiate a common communication method. QueryInterface is also the key to solving the challenge of versioning, which is discussed in greater detail in the following paragraphs.

Interface Versioning

Because components and applications can be built and published independently, an interface that has been published is immutable—no changes to syntax or semantics are allowed. Changing a published interface is not permitted; even changes to an interface's number of methods could cause damage. For instance, a new client application could erroneously read an interface as containing five methods, and unsuccessfully attempt to call an obsolete component containing only four methods, thus a program error would occur. COM interfaces are therefore immutable to avoid such conflicts.

Now that we have said an interface cannot be changed, a new interface must actually be defined to "version" an interface. Existing clients are probably incompatible with new interfaces, since the original interface continues to exist, so these clients are unaffected by whether or not components implement the interfaces. New clients can implement support for new interfaces and access new features when the clients communicate with new components. If a new client happens to access an older component, it can use QueryInterface to safely detect that the component does not support the new interface and avoid using the new features.

In summary, once a COM interface is defined it cannot be changed, so to create a new version, the new interface is added to the component and the previous interface is also maintained.

Object Lifetime Management

Assuming that an object has been created on a client computer holding an interface pointer to the object, it seems logical that this object could be destroyed on the client on which it was created. However, the process of destroying an object that is no longer needed is complicated. For instance, one client can use QueryInterface to obtain multiple interface pointers to a single object. This client may not be able to track when it had finished using all interface pointers needed to safely destroy the object. Another client could potentially need to use the object after it was marked for deletion. No single client can distinguish when all clients have finished using the object except the object itself—with some assistance from each client.

To address the issues involved in destroying an object, IUnknown provides a third feature—object lifetime management, commonly referred to as reference counting—which tracks the number of clients using an interface. When a new interface pointer to an object is created, the object's reference count must be incremented by calling IUnknown AddRef. A client computer that has finished using an interface pointer calls IUnknown Release to decrement the object's reference count. When the reference count is set to zero, the object destroys itself upon determining that its use is complete. Hence the object lifetime management feature neatly solves the problems of both a single client with multiple interface pointers as well as multiple independent clients. With this feature implemented, the client computer's only tasks are to create an object to get an interface pointer, use the pointer to make method calls, and release the pointer using IUnknown Release.

Classes

When considering classes, it should be remembered that all COM objects are instances of COM classes. A COM class is simply a named implementation of one or more COM interfaces. A COM class is named using a class identifier (CLSID), which is a type of GUID. Like IIDs, CLSIDs are guaranteed to be unique, but are difficult to use. Therefore, COM classes can also have string names, called programmatic identifiers (ProgID).

Every COM class has an associated class object, also known as a class factory, which has the ability to create instances of a single COM class. The COM spe-cification defines a standard API function for creating class objects (CoGetClassObject), and a standard interface for communicating with class objects (IClassFactory). Thus, clients need only one mechanism to create any type of COM object. The most important method of IClassFactory is CreateInstance, which creates an object and returns a specified interface pointer. A client can create a COM object simply by calling CoGetClassObject to capture an IClassFactory interface pointer. Similarly, a client can create such an object by calling IClassFactory CreateInstance to send an interface pointer to the object, then releasing the IClassFactory interface pointer. Because this procedure occurs often in COM applications, COM provides a wrapper function that lets developers perform this procedure with one call: CoCreateInstanceEx.

COM classes differ from most language-based classes in that, once an object has been created, its class is irrelevant to the client. All interaction with the object occurs through public interface pointers, which don't recognize the private implementation class used to create the object. This rigorous separation of interface and implementation is a key feature of COM. This "black box" concept greatly simplifies the client coding effort because the client need not know how the component works, just that they do work.

Components

A COM component is a binary unit of software that can be used to create COM objects. For a given CLSID, a component will include the COM class, the code to implement the class object, and usually the code needed to create appropriate entries in a system registry.

NOTE
Although components are sometimes called servers, we avoid confusion with server computers by maintaining the original term, "component," throughout this chapter.

The Microsoft Windows 95, Windows 98, and Microsoft Windows NT platforms allow three basic packaging methods for COM components: Windows services, executable files, or DLLs. Components are built as Windows services in situations where the components must always be running, even if no one is logged on to the host computer. Windows executable files are often used where an application provides a UI in addition to furnishing COM objects. Microsoft Word is an example of a COM component built as an executable file. In most other scenarios, components are packaged as DLLs. In particular, most components used to construct three-service layered applications will be packaged as DLLs. The Microsoft ActiveX controls used in a presentation layer are DLLs, as are all business service components that run within the MTS environment.

Another way to categorize components is by their location relative to the client, as described in the three categories listed below:

  • In-process components These components run within the same process as the client. All in-process components are implemented as DLLs.
  • Local components Local components run in separate processes, all on the client computer. A local component can be an executable file or a Windows service.
  • Remote components Remote components operate on computers entirely separate from the client. Remote components can be executable files, Windows services, or DLLs. To run a DLL component remotely, a remote computer would implement a surrogate process, or an application run on a remote computer capable of running DLL components. Both COM and MTS provide standard surrogates for DLL components.

For the remainder of this section relating to COM, we will focus on components implemented as DLLs, as these are most prevalent in N-tier, or service-layered, applications.

DLL Component Structure

In addition to implementing COM classes and class objects provided by all types of components, DLL components are expected to implement four well-known entry points:

  • DllGetClassObject returns an interface pointer to a class object for a specified component-implemented CLSID.
  • DllCanUnloadNow indicates whether any objects created by a component are still active. If so, the DLL needs to remain in memory; otherwise, the DLL can be unloaded, allowing for computer resource conservation.
  • DllRegisterServer writes all registry entries required for all COM classes implemented in the component.
  • DllUnregisterServer removes all registry entries created by DllRegisterServer.

The COM run time calls DllGetClassObject and DllCanUnloadNow; applications should never need to call these functions directly. Installation programs and developer tools usually call DllRegisterServer and DllUnregisterServer.

Threading

COM supports multiple threading models for components. A threading model defines on which threads a component's objects can run, and also specifies how COM will synchronize access to the objects. On the Windows 95, Windows 98, and Windows NT platforms, COM components run in a multi-threaded environment. Components must be written to run correctly in such an environment.

All COM threading models are based on the notion of apartments. An apartment is an execution context for objects. Every object resides in exactly one apartment for its entire lifetime; one or more apartments reside within each process. All threads in a given process must be associated with an apartment before making COM calls, by calling CoInitialize or CoInitializeEx.

All calls to an object are made in the object's apartment. If the application responsible for calling runs in a different apartment, COM synchronizes access to the object's apartment. In addition, cross-apartment calls must be marshaled when using a single-threaded apartment (STA) model. More fully detailed in the "Remote Activation and Marshaling" section later in this chapter, marshaling essentially means that COM intercepts a call, packages the call stack into a standard format, does some work, and then converts the package back to a method call in the object's apartment. In-process, cross-apartment calls can substantially impact performance, highlighting the importance of understanding COM threading models.

In Windows 95, Windows 98, and Windows NT 4.0, COM supports two types of apartments. A single-threaded apartment (STA) is associated with one thread for the lifetime of an apartment. Such an apartment is created when a thread calls CoInitialize or CoInitializeEx. One process can have multiple STAs; the first STA created is the main STA. Each process can also have one multi-threaded apartment. Multiple threads can be associated with a multi-threaded apartment model. The registry key ThreadModel=FREE denotes a multi-threaded apartment model, also referred to as a free-threaded model.

By default, if the registry key ThreadModel is not set, an STA has a single thread associated with it for its entire lifetime, and apartment model objects residing in an STA will never be accessed concurrently, as they will always run on one thread. No matter how many objects are in this apartment, only one method call will execute at a time, and only a single instance of each particular object may exist in the STA at any given point in time. Thus, object developers are helped considerably as it eliminates the need to synchronize access to per-object state; if necessary, thread-local storage can be used. To provide better scalability, the registry key ThreadModel=Apartment can be set to allow multiple instances of an object to exist in an STA at any point in time. To implement this apartment value, the component's global data and DLL entry points must be coded as thread safe.

Synchronization in the STA is based on Windows messages. Calls are queued as special messages and processed by a COM-created hidden window. As a result, threads associated with an STA must have a message pump, which is a special program loop that retrieves Windows messages from the current thread's message queue, translates them, and dispatches the messages to other parts of the application. If the thread does not have a message pump, no calls will be processed. The STA synchronization model does not prevent re-entrance; it just provides thread safety. This model is exactly the same as that used by window procedures. During method calls to other apartments or processes, COM continues to pump messages so that the STA can process incoming calls and ensure that the thread's windows are responsive. Called objects can call to other objects in the STA without fear of deadlock.

Components complying with the apartment model are easy to write, but concurrency constraints can create an unacceptable performance bottleneck. In this case, such components may need to support the free-threaded model. COM does not synchronize access to objects in the multi-threaded apartment. Threads are dynamically allocated as needed to service all concurrent calls into the multi-threaded apartment. Thus, it is possible for free-threaded objects to be accessed concurrently by multiple threads, which can improve performance.

Writing thread-safe code can be difficult. Both global variables and per-object state must be protected. Developers must also be concerned as to whether functions from run-time or statically linked libraries are thread-safe. COM provides an important advantage in that it provides a choice of threading models when writing code. COM also resolves any mismatches between an object and caller-supported threading models.

This feature is particularly interesting for in-process components. In-process components normally use their client process threads instead of creating new threads. For the client process to create a COM object, it must have already called CoInitialize or CoInitializeEx to establish the apartment associated with the calling thread. But then how does COM ensure that the caller's apartment is compatible with the threading model supported by the created object? When a component is created, it specifies the threading model it supports using a named registry value on the InprocServer32 key, as shown here:

HKCR\CLSID\{45D3F4B1-DB76-11d1-AA06-0040052510F1}\InprocServer32    @="salestax.dll"    ThreadingModel="Apartment" 

COM uses the ThreadingModel value to determine which apartment an object will be created in, as shown in Table 8.1. For example, if the caller's apartment is an STA and the component's ThreadingModel is Apartment, the object will be created in the caller's STA. All calls from caller to object will be direct calls; no marshaling is needed. If the caller's apartment is a multi-threaded apartment and the component's ThreadingModel is Apartment, the object will be created in a new STA. All calls from caller to object will be marshaled.

Table 8.1 In-process server threading model options

Caller's apartment
ThreadingModel value Main single-threaded apartment Single-threaded apartment Multi-threaded apartment
Unspecified Main STA Main STA Main STA
Apartment Main STA Calling STA New STA created
Free Multi-threaded apartment Multi-threaded apartment Multi-threaded apartment
Both Main STA Calling STA Multi-threaded apartment

Most applications and components available today either support the apartment model or are single-threaded (that is, they use the main STA). Apartment model components offer a nice balance of ease of development and performance. As discussed in more detail later in this chapter, MTS provides features to help apartment model components scale to support large numbers of users.

COM Programming Model

The programming model specified by COM is simple and powerful. Sometimes it's difficult to see the basic model's simplicity and elegance underneath all the services built on top of it. So we won't talk about any of those services here; instead, we'll focus on the programming model itself.

COM, OLE, and ActiveX

Developers might be more familiar with OLE and ActiveX than with COM, and might be confused about what technologies these terms refer to and how they are related. This wouldn't be surprising: Microsoft has changed its definitions of these terms over the past couple of years even though the technologies themselves have not changed. Here's a quick run-down:

  • COM The fundamental component object model, introduced in 1992. The COM specification is available on the Microsoft Web site (at www.microsoft.com/com/resources/specs.ASP), and only those items defined in the specification are part of COM proper.
  • OLE Built on top of COM and the mechanism used for compound documents. An example of using OLE is when a Microsoft Excel spreadsheet is inserted into a Word document.
  • ActiveX Originally introduced with Microsoft's COM-based Internet technologies in 1996; essentially a marketing label used to identify these technologies. Then things got a little crazy, and everything COM-based got grouped under the ActiveX umbrella. That just confused everyone. Today some degree of normalcy has returned and the term ActiveX is used only when referring to ActiveX controls, a specific technology built on top of COM for programmatic controls. When developers put a control on a Visual Basic form or embed an <OBJECT> tag in an HTML page, they use ActiveX controls.


Microsoft Corporation - Analyzing Requirements and Defining Solutions Architecture. MCSD Training Kit
Microsoft Corporation - Analyzing Requirements and Defining Solutions Architecture. MCSD Training Kit
ISBN: N/A
EAN: N/A
Year: 1999
Pages: 182

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net