The Microsoft Component Object Model
ArcObjects is based on Microsoft's Component Object Model (COM). End users of ArcGIS applications don't necessarily have to understand COM, but if you're a developer intent on developing applications based on ArcObjects or extending the existing ArcGIS applications using ArcObjects, an understanding of COM is a requirement even if you plan to use the .NET or Java API and not COM specifically. The level of understanding required depends on the depth of customization or development you want to undertake. Although this topic does not cover the entire COM environment, it provides both Visual Basic and Visual C++ developers with sufficient knowledge to be effective in using ArcObjects. There are many coding tips and guidelines that should make your work with ArcObjects more effective.
COMPONENTS, OBJECTS, CLIENTS AND SERVERS
CLASS FACTORY
GLOBALLY UNIQUE IDENTIFIERS
COM CLASSES AND INTERFACES
INSIDE INTERFACES
THE IUNKNOWN INTERFACE
INTERFACE DEFINITION LANGUAGE
TYPE LIBRARY
INBOUND AND OUTBOUND INTERFACES
Dispatch event interfaces
Default interfaces
IDispatch interface
INTERFACE INHERITANCE
AGGREGATION AND CONTAINMENT
THREADS, APARTMENTS, AND MARSHALING
COMPONENT CATEGORY
COM AND THE REGISTRY
AUTOMATION
CLASS FACTORY
GLOBALLY UNIQUE IDENTIFIERS
COM CLASSES AND INTERFACES
INSIDE INTERFACES
THE IUNKNOWN INTERFACE
INTERFACE DEFINITION LANGUAGE
TYPE LIBRARY
INBOUND AND OUTBOUND INTERFACES
Dispatch event interfaces
Default interfaces
IDispatch interface
INTERFACE INHERITANCE
AGGREGATION AND CONTAINMENT
THREADS, APARTMENTS, AND MARSHALING
COMPONENT CATEGORY
COM AND THE REGISTRY
AUTOMATION
Before discussing COM specifically, it is worth considering the wider use of software components in general. There are a number of factors driving the motivation behind software components, but the principal one is the fact that software development is a costly and time-consuming venture.
In an ideal world, it would be possible to write a piece of code once and reuse it again and again using a variety of development tools, even in circumstances that the original developer did not foresee. Ideally, changes to the code's functionality made by the original developer could be deployed without requiring existing users to change or recompile their code.
Early attempts at producing reusable chunks of code revolved around the creation of class libraries, usually developed in C++. These early attempts suffered from several limitations, notably difficulty of sharing parts of the system (it is difficult to share binary C++ components— most attempts have only shared source code), problems of persistence and updating C++ components without recompiling, lack of good modeling languages and tools, and proprietary interfaces and customization tools.
To counteract these and other problems, many software engineers have adopted component-based approaches to system development. A software component is a binary unit of reusable code.
Several different but overlapping standards have emerged for developing and sharing components. For building interactive desktop applications, Microsoft's COM is the de facto standard. On the Internet, JavaBeans is viable technology. At a coarser grain appropriate for application-level interoperability, the Object Management Group (OMG) has specified the common object request broker architecture (CORBA).
ESRI chose COM as the component technology for ArcGIS because it is a mature technology that offers good performance, many of today's development tools support it, and there is a multitude of third-party components that can be used to extend the functionality of ArcObjects.
To understand COM (and, therefore, all COM-based technologies), it's important to realize that it isn't an object-oriented language but a protocol, or standard. COM is more than just a technology; it is a methodology of software development. COM defines a protocol that connects one software component, or module, with another. By making use of this protocol, it's possible to build reusable software components that can be dynamically interchanged in a distributed system.
COM also defines a programming model known as interface-based programming. Objects encapsulate the manipulation methods and the data that characterizes each instantiated object behind a well-defined interface. This promotes structured and safe system development, since the client of an object is protected from knowing any details of how a particular method is implemented. COM doesn't specify how an application should be structured. As an application programmer working with COM, language, structure, and implementation details are left up to you.
The key to the success of components is that they implement, in a practical way, many of the object-oriented principles now commonly accepted in software engineering. Components facilitate software reuse because they are self-contained building blocks that can easily be assembled into larger systems.
COM does specify an object model and programming requirements that enable COM objects to interact with other COM objects. These objects can be within a single process, in other processes, or even on remote machines. They can be written in other languages and may have been developed in different ways. That is why COM is referred to as a binary specification or standard—it is a standard that applies after a program has been translated to binary machine code.
COM allows these objects to be reused at a binary level, meaning that third- party developers do not require access to source code, header files, or object libraries to extend the system even at the lowest level.
COMPONENTS, OBJECTS, CLIENTS, AND SERVERS
Different texts use the terms components, objects, clients, and servers to mean different things. (To add to confusion, various texts refer to the same thing using all these terms.) Therefore, it is worthwhile to define the terminology.
COM is a client/server architecture. The server (or object) provides some functionality, and the client uses that functionality. COM facilitates the communication between the client and the object. An object can, at the same time, be a server to a client and a client of some other object's services.
Objects are instances of COM classes that make services available for use by a client. Hence, it is normal to talk of clients and objects instead of clients and servers. These objects are often referred to as COM objects and component objects. This book will refer to them simply as objects.
The client and its servers can exist in the same process or in a different process space. In-process servers are packaged in Dynamic Link Library (DLL) form, and these DLLs are loaded into the client's address space when the client first accesses the server. Out-of-process servers are packaged in executables (EXE) and run in their own address space. COM makes the differences transparent to the client.
When creating COM objects, the developer must be aware of the type of server that the objects will reside in, but if the creator of the object has implemented them correctly, the packaging does not affect the use of the objects by the client.
There are pros and cons to each method of packaging that are symmetrically opposite. DLLs are faster to load into memory, and calling a DLL function is faster. EXEs, on the other hand, provide a more robust solution (if the server fails, the client will not crash), and security is better handled since the server has its own security context.
In a distributed system, EXEs are more flexible, and it does not matter if the server has a different byte ordering from the client. The majority of ArcObjects servers are packaged as in-process servers (DLLs). Later, you will see the performance benefits associated with in-process servers.
In a COM system, the client, or user of functionality, is completely isolated from the provider of that functionality, the object. All the client needs to know is that the functionality is available; with this knowledge, the client can make method calls to the object and expect the object to honor them. In this way, COM is said to act as a contract between client and object. If the object breaks that contract, the behavior of the system will be unspecified. In this way, COM development is based on trust between the implementer and the user of functionality.
In the ArcGIS applications, there are many objects that provide, via their interfaces, thousands of properties and methods. When you use the ESRI object libraries, you can assume that all these properties and interfaces have been fully implemented, and if they are present on the object diagrams, they are there to use.
CLASS FACTORY
Within each server there is an object called a class factory that the COM runtime interacts with to instantiate objects of a particular class. For every corresponding COM class, there is a class factory. Normally, when a client requests an object from a server, the appropriate class factory creates a new object and passes out that object to the client.
A server is a binary file that contains all the code required by one or more COM classes. This includes both the code that works with COM to instantiate objects into memory and the code to perform the methods supported by the objects contained within the server.
Although this is the normal implementation, it is not the only implementation possible. The class factory can also create an instance of the object the first time and, with subsequent calls, pass out the same object to clients. This type of implementation creates what is known as a singleton object since there is only one instance of the object per process.
GLOBALLY UNIQUE IDENTIFIERS
A distributed system potentially has thousands of interfaces, classes, and servers, all of which must be referenced when locating and binding clients and objects together at runtime. Clearly, using human-readable names would lead to the potential for clashes; hence, COM uses globally unique identifiers (GUIDs), 128-bit numbers that are virtually guaranteed to be unique in the world. It is possible to generate 10 million GUIDs per second until the year 5770 A.D., and each one would be unique.
The acronym GUID is commonly pronounced "gwid".
The COM API defines a function that can be used to generate GUIDs; in addition, all COM-compliant development tools automatically assign GUIDs when appropriate. GUIDs are the same as Universally Unique Identifiers (UUIDs), defined by the Open Group's Distributed Computing Environment (DCE) specification. Below is a sample GUID in registry format.
{E6BDAA76-4D35-11D0-98BE-00805F7CED21}
GUIDGEN.EXE is a utility that ships with Microsoft's Visual Studio and provides an easy-to-use user interface for generating GUIDs. It can be found in the directory \Common\Tools.
COM CLASSES AND INTERFACES
Developing with COM means developing using interfaces, the so-called interface-based programming model. All communication between objects is made via their interfaces. COM interfaces are abstract, meaning there is no implementation associated with an interface; the code associated with an interface comes from a class implementation. The interface sets which requests can be made of an object that chooses to implement the interface.
How an interface is implemented differs among objects. Thus, the objects inherit the type of interface, not its implementation, which is called type inheritance. Functionality is modeled abstractly with the interfaces and implemented within a class implementation. Classes and interfaces are often referred to as the "what" and "how" of COM. The interface defines what an object can do, and the class defines how it is done.
This is a simplified portion of the geodatabase object model showing type inheritance among abstract classes, coclasses, and instantiation of classes.
COM classes provide the code associated with one or more interfaces, thus encapsulating the functionality entirely within the class. Two classes can have the same interface, but they may implement them quite differently. By implementing these interfaces in this way, COM displays classic object-oriented polymorphic behavior. COM does not support the concept of multiple inheritance; however, this is not a shortcoming since individual classes can implement multiple interfaces.
Within ArcObjects are three types of classes that the developer must be aware of: abstract classes, coclasses, and classes. An abstract class cannot be created; it is solely a specification for instances of subclasses (through type inheritance). ArcObjects Dataset and Geometry classes are examples of abstract classes. An object of type Geometry cannot be created, but an object of type Polyline can. This Polyline object, in turn, implements the interfaces defined within the Geometry base class, hence any interfaces defined within object-based classes are accessible from the coclass.
A coclass is a publicly creatable class. In other words, it is possible for COM to create an instance of that class and give the resultant object to the client to use the services defined by the interfaces of that class. A class cannot be publicly created, but objects of this class can be created by other objects within ArcObjects and given to clients to use.
Below is a diagram that illustrates the polymorphic behavior exhibited in COM classes when implementing interfaces. Notice that both the Human and Parrot classes implement the ITalk interface. The ITalk interface defines the methods and properties, such as StartTalking, StopTalking, or Language, but clearly the two classes implement these differently.
This diagrams shows how common behavior, expressed as interfaces, can be shared among multiple objects, animals in this example, to support polymorphism.
INSIDE INTERFACES
COM interfaces are how COM objects communicate with each other. When working with COM objects, the developer never works with the COM object directly but gains access to the object via one of its interfaces. COM interfaces are designed to be a grouping of logically related functions. The virtual functions are called by the client and implemented by the server; in this way, an object's interfaces are the contract between the client and object. The client of an object is holding an interface pointer to that object. This interface pointer is referred to as an opaque pointer since the client cannot gain any knowledge of the implementation details within an object or direct access to an object's state data. The client must communicate through the member functions of the interface. This allows COM to provide a binary standard through which all objects can effectively communicate.
Interfaces allow developers to model functionality abstractly. Visual C++ developers see interfaces as collections of pure virtual functions, while Visual Basic developers see interfaces as collections of properties, functions, and subroutines.
The concept of the interface is fundamental in COM. The COM Specification (Microsoft, 1995) emphasizes these four points when discussing COM interfaces:
- An interface is not a class. An interface cannot be instantiated by itself since it carries no implementation.
- An interface is not an object. An interface is a related group of functions and is the binary standard through which clients and objects communicate.
- Interfaces are strongly typed. Every interface has its own interface identifier, thereby eliminating the possibility of a collision between interfaces of the same human-readable name.
- Interfaces are immutable. Interfaces are never versioned. Once defined and published, an interface cannot be changed.
Once an interface has been published it is not possible to change the external signature of that interface. It is possible at any time to change the implementation details of an object that exposes an interface. This change may be a minor bug fix or a complete reworking of the underlying algorithm; the clients of the interface do not care since the interface appears the same to them. This means that when upgrades to the servers are deployed in the form of new DLLs and EXEs, existing clients need not be recompiled to make use of the new functionality. If the external signature of the interface is no longer sufficient, a new interface is created to expose the new functions. Old or deprecated interfaces are not removed from a class to ensure all existing client applications can continue to communicate with the newly upgraded server. Newer clients will have the choice of using the old or new interfaces.
An interface's permanence is not restricted to simply its method signatures but extends to its semantic behavior as well. For example, an interface defines two methods, A and B, with no restrictions placed on their use. It breaks the COM contract if at a subsequent release Method A requires that method B be executed first. A change like this would force possible recompilations of clients.
THE IUNKNOWN INTERFACE
All COM interfaces derive from the IUnknown interface, and all COM objects must implement this interface. The IUnknown interface performs two tasks: it controls object lifetime and provides runtime type support. It is through the IUnknown interface that clients maintain a reference on an object while it is in use—leaving the actual lifetime management to the object itself.
The name IUnknown came from an 1988 internal Microsoft paper called Object Architecture: Dealing with the Unknown – or – Type Safety in a Dynamically Extensible Class Library.
Object lifetime is controlled with two methods, AddRef and Release, and an internal reference counter. Every object must have an implementation of IUnknown to control its own lifetime. Anytime an interface pointer is created or duplicated, the AddRef method is called, and when the client no longer requires this pointer, the corresponding Release method is called. When the reference count reaches zero, the object destroys itself.
Clients also use IUnknown to acquire other interfaces on an object. QueryInterface is the method that a client calls when another interface on the object is required. When a client calls QueryInterface, the object provides an interface and calls AddRef. In fact, it is the responsibility of any COM method that returns an interface to increment the reference count for the object on behalf of the caller. The client must call the Release method when the interface is no longer needed. The client calls AddRef explicitly only when an interface is duplicated.
The method QueryInterface is often referred to by the abbreviation QI.
When developing a COM object, the developer must obey the rules of QueryInterface. These rules dictate that interfaces for an object are symmetrical, transitive, and reflexive and are always available for the lifetime of an object. For the client this means that, given a valid interface to an object, it is always valid to ask the object, via a call to QueryInterface, for any other interface on that object including itself. It is not possible to support an interface and later deny access to that interface, perhaps because of time or security constraints. Other mechanisms must be used to provide this level of functionality. Some classes support the concept of optional interfaces. Depending on the coclass, they may optionally implement an interface; this does not break this rule since the interface is either always available or always not available on the class.
The rules of QueryInterface dictate that interfaces of an object are reflexive, symmetrical, and transitive. It is always possible, holding a valid interface pointer on an object, to get any other interface on that object.
When requested for a particular interface, the QueryInterface method can return an already assigned piece of memory for that requested interface, or it can allocate a new piece of memory and return that. The only case when the same piece of memory must be returned is when the IUnknown interface is requested. When comparing two interface pointers to see if they point to the same object, it is important that a simple comparison not be performed. To correctly compare two interface pointers to see if they are for the same object, they both must be queried for their IUnknown interface, and the comparison must be performed on the IUnknown pointers. In this way, the IUnknown interface is said to define a COM object's identity.
Since IUnknown is fundamental to all COM objects, in general there are no references to IUnknown in any of the ArcObjects documentation and class diagrams.
It's good practice in Visual Basic to call Release explicitly by assigning an interface equal to Nothing to release any resources it's holding. Even if you don't call Release, Visual Basic will automatically call it when you no longer need the object—that is, when it goes out of scope. With global variables, you must explicitly call Release. In Visual Basic, the system performs all these reference-counting operations for you, making the use of COM objects relatively straightforward.
In C++, however, you must increment and decrement the reference count to allow an object to correctly control its own lifetime. Likewise, the QueryInterface method must be called when asking for another interface. In C++ the use of smart pointers simplifies much of this. These smart pointers are class based and, hence, have appropriate constructors, destructors, and overloaded operators to automate much of the reference counting and query interface operations.
Smart pointers are a class-based smart type.
INTERFACE DEFINITION LANGUAGE
Microsoft Interface Definition Language (MIDL) is used to describe COM objects including their interfaces. This MIDL is an extension of the Interface Definition Language (IDL) defined by DCE, where it was used to define remote procedure calls between clients and servers. The MIDL extensions include most of the Object Definition Language (ODL) statements and attributes. ODL was used in the early days of OLE automation for the creation of type libraries.
MIDL is commonly referred to as IDL.
The IDL defines the public interface that developers use when working with ArcObjects. When compiled, the IDL creates a type library.
TYPE LIBRARY
A type library is best thought of as a binary version of an IDL file. It contains a binary description of all coclasses, interfaces, methods, and types contained within a server or servers. There are several COM interfaces provided by Microsoft that work with type libraries. Two of these interfaces are ITypeInfo and ITypeLib. By utilizing these standard COM interfaces, various development tools and compilers can gain information about the coclasses and interfaces supported by a particular library.
To support the concept of a language-independent development set of components, all relevant data concerning the ArcObjects libraries is shipped inside type libraries. There are no header files, source files, or object files supplied or needed by external developers.
INBOUND AND OUTBOUND INTERFACES
Interfaces can be either inbound or outbound. An inbound interface is the most common kind—the client makes calls to functions within the interface contained on an object. An outbound interface is one in which the object makes calls to the client—a technique analogous to the traditional callback mechanism.
In the diagrams in this book and the ArcObjects object model diagrams, outbound interfaces are depicted with a solid circle on the interface jack.
There are differences in the ways these interfaces are implemented. The implementer of an inbound interface must implement all functions of the interface; failure to do so breaks the contract of COM. This is also true for outbound interfaces. If you use Visual Basic, you don't have to implement all functions present on the interface since it provides stub methods for the methods you don't implement. On the other hand, if you use C++, you must implement all the pure virtual functions to compile the class.
Connection points is a specific methodology for working with outbound COM interfaces. The connection point architecture defines how the communication between objects is set up and taken down. Connection points are not the most efficient way of initializing bidirectional object communication, but they are in common use because many development tools and environments support them.
Dispatch event interfaces
There are some objects within ArcObjects that support two outbound event interfaces that look similar to the methods they support. Examples of two such interfaces are the IDocumentEvents and the IDocumentEventsDisp. The "Disp" suffix denotes a pure dispatch interface. These dispatch interfaces are used by VBA when dealing with certain application events such as loading documents. A VBA programmer works with the dispatch interfaces, while a developer using another development language uses the nonpure dispatch interface.
Default interfaces
Every COM object has a default interface that is returned when the object is created if no other interface is specified. All the objects within the ESRI object libraries have IUnknown as their default interface, with a few exceptions.
The reason for making IUnknown the default interface is because the VB object browser hides information for the default interface. The fact that it hides IUnknown is not important for VB developers.
The default interface of the Application object for both ArcCatalog and ArcMap is the IApplication interface. These uses of non-IUnknown default interfaces are a requirement of Visual Basic for Applications and are found on the ArcMap and ArcCatalog application-level objects. This means that variables that hold interface pointers must be declared in a certain way. When COM objects are created, any of the supported interfaces can be requested at creation time.
IDispatch interface
Binding is the term given to the process of matching the location of a function given a pointer to an object.
COM supports three types of binding:
-
Late. This is where type discovery is left until runtime. Method calls made by the client but not implemented by the object will fail at execution time.
-
ID. Method IDs are stored at compile time, but execution of the method is still performed through a higher-level function.
-
Custom vTable (early). Binding is performed at compile time. The client can then make method calls directly into the object.
The IDispatch interface supports late- and ID-binding languages. The IDispatch interface has methods that allow clients to ask the object what methods it supports.
Assuming the required method is supported, the client executes the method by calling the IDispatch::Invoke method. This method, in turn, calls the required method and returns the status and any parameters back to the client on completion of the method call.
Clearly, this is not the most efficient way to make calls on a COM object. Late binding requires a call to the object to retrieve the list of method IDs; the client must then construct the call to the Invoke method and call it. The Invoke method must then unpack the method parameters and call the function.
All these steps add significant overhead to the time it takes to execute a method. In addition, every object must have an implementation for IDispatch, which makes all objects larger and adds to their development time.
ID binding offers a slight improvement over late binding in that the method IDs are cached at compile time, which means the initial call to retrieve the IDs is not required. However, there is still significant call overhead because the IDispatch::Invoke method is still called to execute the required method on the object.
Early binding, often referred to as custom vTable binding, does not use the IDispatch interface. Instead, a type library provides the required information at compile time to allow the client to know the layout of the server object. At runtime, the client makes method calls directly into the object. This is the fastest method of calling object methods and also has the benefit of compile-time type checking.
The following table shows the number of function calls that can be made per second on a typical Pentium III machine.
Binding type
|
In process DLL
|
Out of process DLL
|
Late binding
|
22,250
|
5,000
|
Custom vTable binding
|
825,000
|
20,000
|
Objects that support both IDispatch and custom vTable are referred to as dual interface objects. The object classes within the ESRI object libraries do not implement the IDispatch interface; this means that these object libraries cannot be used with late-binding scripting languages, such as JavaScript or VBScript, since these languages require that all COM servers accessed support the IDispatch interface.
These diagrams summarize the custom and IDispatch interfaces for two classes in ArcObjects. The layout of the vTable displays the differences. It also illustrates the importance of implementing all methods—if one method is missing, the vTable will have the wrong layout, and hence, the wrong function pointer would be returned to the client, resulting in a system crash.
Careful examination of the ArcGIS class diagrams indicates that the Application objects support IDispatch because there is a requirement in VBA for the IDispatch interface.
All ActiveX controls support IDispatch. This means it is possible to use the various ActiveX controls shipped with ArcObjects to access functionality from within scripting environments.
INTERFACE INHERITANCE
An interface consists of a group of methods and properties. If one interface inherits from another, then all of the methods and properties in the parent are directly available in the inheriting object.
Interfaces that directly inherit from an interface other than IUnknown cannot be implemented in VB.
The underlying principle here is interface inheritance, rather than the implementation inheritance you may have seen in languages such as SmallTalk and C++. In implementation inheritance, an object inherits actual code from its parent; in interface inheritance, it's the definitions of the methods of the object that are passed on. The coclass that implements the interfaces must provide the implementation for all inherited interfaces.
Implementation inheritance is not supported in a heterogeneous development environment because of the need to access source and header files. For reuse of code, COM uses the principles of aggregation and containment. Both of these are binary-reuse techniques.
AGGREGATION AND CONTAINMENT
For a third-party developer to make use of existing objects, using either containment or aggregation, the only requirement is that the server housing the contained or aggregated object is installed on both the developer and target release machines. Not all development languages support aggregation.
The simplest form of binary reuse is containment. Containment allows modification of the original object's method behavior but not the method's signature. With containment, the contained object (inner) has no knowledge that it is contained within another object (outer). The outer object must implement all the interfaces supported by the inner. When requests are made on these interfaces, the outer object simply delegates them to the inner. To support new functionality, the outer object can either implement one of the interfaces without passing the calls on or implement an entirely new interface in addition to those interfaces from the inner object.
COM aggregation involves an outer object that controls which interfaces it chooses to expose from an inner object. Aggregation does not allow modification of the original object's method behavior. The inner object is aware that it is being aggregated into another object and forwards any QueryInterface calls to the outer (controlling) object so the object as a whole obeys the laws of COM.
To the clients of an object using aggregation, there is no way to distinguish which interfaces the outer object implements and which interfaces the inner object implements.
Custom features make use of both containment and aggregation. The developer aggregates the interfaces where no customizations are required and contains those that are to be customized. The individual methods on the contained interfaces can then either be implemented in the customized class, thus providing custom functionality, or the method call can be passed to the appropriate method on the contained interface.
Aggregation is important in this case since there are some hidden interfaces defined on a feature that cannot be contained.
Visual Basic 6 does not support aggregation, so it can't be used to create custom features.
THREADS, APARTMENTS, AND MARSHALLING
A thread is a process flow through an application. There are potentially many threads within Windows applications. An apartment is a group of threads that works with contexts within a process. With COM+, a context belongs to one apartment. There are potentially many types of contexts; security is an example of a type of context. Before successfully communicating with each other, objects must have compatible contexts.
Although an understanding of apartments and threading is not essential in the use of ArcObjects, basic knowledge will help you understand some of the implications with certain development environments highlighted later in the Getting Started section.
COM supports two types of apartments: single-threaded apartments (STA) and multithreaded apartments (MTA). COM+ supports the additional thread-neutral apartment (TNA). A process can have any number of STAs; each process creates one STA called the main apartment. Threads that are created as apartments are placed in an STA. All user interface code is placed in an STA to prevent deadlock situations. A process can only have one MTA. A thread that is started as multithreaded is placed in the MTA. The TNA has no threads permanently associated with it; rather, threads enter and leave the apartment when appropriate.
In-process objects have an entry in the registry, the ThreadingModel, that informs the COM service control manager (SCM) into which apartment to place the object. If the object's requested apartment is compatible with the creator's apartment, the object is placed in that apartment; otherwise, the SCM will find or create the appropriate apartment. If no threading model is defined, the object will be placed in the main apartment of the process. The ThreadingModel registry entry can have the following values:
-
Apartment. Object must be executed within the STA. Normally used by UI objects.
-
Free. Object must be executed within the MTA. Objects creating threads are normally placed in the MTA.
-
Both. Object is compatible with all apartment types. The object will be created in the same apartment as the creator.
-
Neutral. Objects must execute in the TNA. Used by objects to ensure there is no thread switch when called from other apartments. This is only available under COM+.
Think of the SCM (pronounced scum) as the COM runtime environment. The SCM interacts with objects, servers, and the operating system and provides the transparency between clients and the objects with which they work.
Marshalling enables a client to make interface function calls to objects in other apartments transparently. Marshalling can occur between COM apartments on different machines, between COM apartments in different process spaces, and between COM apartments in the same process space (STA to MTA, for example). COM provides a standard marshaller that handles function calls that use automation-compliant data types (see table below). Nonautomation data types can be handled by the standard marshaller as long as proxy stub code is generated; otherwise, custom marshalling code is required.
Type
|
Description
|
Boolean
|
Data item that can have the value True or False
|
unsigned char
|
8-bit unsigned data item
|
double
|
64-bit IEEE floating-point number
|
float
|
32-bit IEEE floating-point number
|
int
|
Signed integer, whose size is system dependent
|
long
|
32-bit signed integer
|
short
|
16-bit signed integer
|
BSTR
|
Length-prefixed string
|
CURRENCY
|
8-byte, fixed-point number
|
DATE
|
64-bit, floating-point fractional number of days since Dec 30, 1899
|
SCODE
|
For 16-bit systems - Built-in error that corresponds to VT_ERROR
|
Typedef enum myenum
|
Signed integer, whose size is system dependent
|
Interface IDispatch *
|
Pointer to the IDispatch interface
|
Interface IUnknown *
|
Pointer to an interface that does not derive from IDispatch
|
dispinterface Typename *
|
Pointer to an interface derived from IDispatch
|
Coclass Typename *
|
Pointer to a coclass name (VT_UNKNOWN)
|
[oleautomation] interface Typename *
|
Pointer to an interface that derives from IDispatch
|
SAFEARRAY(TypeName)
|
TypeName is any of the above types. Array of these types
|
TypeName*
|
TypeName is any of the above types. Pointer to a type
|
Decimal
|
96-bit unsigned binary integer scaled by a variable power of 10. A decimal data type that provides a size and scale for a number (as in coordinates)
|
COMPONENT CATEGORY
Component categories are used by client applications to find all COM classes of a particular type that are installed on the system efficiently. For example, a client application may support a data export function in which you can specify the output format—a component category could be used to find all the data export classes for the various formats. If component categories are not used, the application has to instantiate each object and interrogate it to see if it supports the required functionality, which is not a practical approach. Component categories support the extensibility of COM by allowing the developer of the client application to create and work with classes that belong to a particular category. If at a later date a new class is added to the category, the client application need not be changed to take advantage of the new class; it will automatically pick up the new class the next time the category is read.
COM AND THE REGISTRY
COM makes use of the Windows system registry to store information about the various parts that compose a COM system. The classes, interfaces, DLLs, EXEs, type libraries, and so forth, are all assigned unique identifiers (GUIDs) that the SCM uses when referencing these components. To see an example of this, run regedit, then open HKEY_CLASSES_ROOT. This opens a list of all the classes registered on the system.
ESRI keys in the Windows system registry
COM makes use of the registry for a number of housekeeping tasks, but the most important and most easily understood is the use of the registry when instantiating COM objects into memory. In the simplest case, that of an in-process server, the steps are as follows:
-
Client requests the services of a COM object.
-
SCM looks for the requested objects registry entry by searching on the class ID (a GUID).
-
DLL is located and loaded into memory. The SCM calls a function within the DLL called DllGetClassObject, passing the desired class as the first argument.
The function DllGetClassObject is the function that makes a DLL a COM DLL. Other functions, such as DllRegisterServer and DllUnregisterServer, are nice to have but not essential for a DLL to function as a COM DLL.
- The class object normally implements the interface IClassFactory. The SCM calls the method CreateInstance on this interface to instantiate the appropriate object into memory.
- Finally, the SCM asks the newly created object for the interface that the client requested and passes that interface back to the client. At this stage, the SCM drops out of the equation, and the client and object communicate directly.
From the above sequence of steps, it is easy to imagine how changes in the object's packaging (DLL versus EXE) make little difference to the client of the object. COM handles these differences.
AUTOMATION
Automation is the technology used by individual objects or entire applications to provide access to their encapsulated functionality via a late-bound language. Commonly, automation is thought of as writing macros, where these macros can access many applications for a task to be done. ArcObjects, as already stated, does not support the IDispatch interface; hence, it cannot be used alone by an automation controller.