Component Object Model (COM) defines a binary standard for components to interact with each other through their interface definitions. COM defines both how the object exposes itself and how this exposure work across processes and across networks. Interface based approach allows to ensure dynamic interoperability of binary objects written in different programming languages. Object lifecycle support in COM loads the server objects on demand if they are not loaded already and releases them whenever it finds out there is no user for this object.
With these objectives at hand, COM defines a set of interfaces and services to accomplish all its goals. Meanwhile, the designers of COM has to keep up backward compatibility to be able to run the previously written programs under the same framework, they had to push creativity and complex solutions in the design.
COM supports interface based programming framework where an object exposes its functionality through an interface definition. Interface based programming separates the definition of object's public view from the implementation of the object (implementation hiding) so that a client invokes methods on the interface without knowing how the implementation looks like. This encapsulated view supports location, platform, programming language transparency, and dynamic binding.
Object oriented programming widely favors the inheritance and poylmorphism. Since interface based programming separated the public view of the object from the implementation, inheritance in interface based programming is called as interface inheritance. When there is no such distinction, inheritance based on implementation (implementation inheritance) supports code reuse and poylmorphism. Code reuse is achieved by defining "is-a" relationship between objects. Common behaviors are implemented in the superclass and specialized objects (subclasses) inherit this implementation without writing a line of code. Subclasses can also provide their own implementations for some methods so that each subclass behaves its own way (polymorphish). After a lot of experience with develeloping large projects with OOP, experts realized that deep hierarchies based on implementation inheritance can create a maintenance nightmare as well as it can stop the evolution of software. This situation is occurred because subclasses started using protected view of the superclass and tightly coupled with each other. From these experiences at hand, COM supports interface inheritance instead of implementation inheritance because COM tries to put components together as loose as possible. Although interface inheritance solves maintenance problems it does not provide code reuse because developer has to provide the necessary code for each inherited function through interface inheritance. Fortunately, creative developers can obtain the code reuse according the implementation languages.
In COM, an interface is a pointer to a block of memory in which the first word is a pointer to a table of function addresses (virtual function table or v-table) as in C++ virtual function table. A client uses the interface only through its v-table since the object may actually be nothing more than a proxy which forwards the method calls to the server object. In COM, two active objects are identical if their IUnknown pointers are identical. If objects are deactivated, then it means nothing.
COM handles errors through 32 bit error code (HRESULT). Each function defined in an interface returns this error code. Some of this structure is reserved for COM's error codes while users can defined their return codes by using lower 16 bit of HRESULT. In large projects, using HRESULT structure to convey the user error codes can be very complicated as those codes should be propagated through several subcomponents. Therefore, using extra parameter in the function definition can be a better practice for error code propagation.
COM uses Universally Unique Identifiers (UUID) to identify interfaces, classes, DLLs, libraries, etc. UUIDs are an unique 128-bit (16 byte) value guaranteed to be unique without consulting a general authority and are generated by Globally Unique Identifier generators such as GUIDGEN. These generators use the current time and the IP address of the machine to generate this numbers. This technique is defined in OSF's DEC specifications. COM defined its own jargon to distinguish which identifier is used for which entity:
CLSID: Class identifier.
IID: Interface identification number.
Once we have these numbers at hand, we have to use them for our advantage. COM uses a Windows Registry to store these numbers and a record related to each entity. Registry is a hierarchy of elements whose each element is called a key. A key can include a set of subkeys, a set of named values, and/or one unnamed value. Subkeys can have other subkeys and values. COM uses HKEY_CLASSES_ROOT branch of Registry tree and ofcourse there are already defined sub branches for special purposes:
Registry is the look up table for COM to find where all these components are, how COM should handle them, what kind of security restriction COM should use, etc.
COM binaries are replaceable as long as it supports the expected interfaces. This feature allows developer to update its component without client knowing which version it is using. Furthermore, with the help of QueryInterface() function in IUnknown interface, a base interface from which all COM components should be derived, a versioning support can be automatically done and client can decide which version it wants to use while the client software developed before these improvements continue their operation without knowing that server object actually has more functionality than it had before.
A client code sometimes do not want to know the interface supported by the object instead it might want to query the object on the runtime and build the method invocations by itself. This technique is known as dynamic method invocation and a client takes the responsibility to pack parameters and make a function call. IDispatch interface of COM is designed for this purpose so that a client marshals parameters for method invocation on the object, gives the name of the function, and calls the Invoke() function. Implementation of the server object supports the IDispatch interface and resolves the parameter of the Invoke() function and makes the function call. This can be a little bit messy because it requires extracting method name and parameters and then invoking the related method and returning the result if required.
Platform and programming-language transparency requires a definition for common data type set which is a common denominator between data types of different languages. Therefore, COM and CORBA define a common data type set.
COM defines an integration infrastructure in which a component can interact with another within a single address space (in-process) or between processes (out-of-process) on the same machine or different machines. This feature provides the necessary support for location transparency where client never knows where the server object resides.
Life cycle is provided with reference counting. COM expects that the client calls a proper function call when it copies an interface pointer and the server object handles this function calls to increment and decrement its reference count. COM expects that the server object releases itself from memory when its count drops zero. Meanwhile, COM keeps reference counts for each Dynamic Link Library loaded by COM, and unloads them from the memory when it realizes that the count is dropped to zero.
COM's persistence is based on Monikers. Monikers provide a persistent naming mechanism and encapsulate implementation specific schemes based on names encoding the location of an object.
All COM components must provide IUnknown base interface which has the following methods in the first three entries of the v-table.
requiredInterfaceID denotes the IID of the required interface and function returns pointer to the implementation of this interface in returnInterfacePtr parameter if the object supports this interface. The type of returnInterfacePtr is void** so that it is possible to return a pointer to any kind of interface.
AddRef() and Release() methods are defined for object lifecycle support in COM. Whenever a client gets the interface pointer of the server object, the reference counter (number of clients which have the pointer to this object) of object is incremented so that system can track down the number of concurrent users of this particular object. Clients are expected to invoke Release() method on interface pointers when they do not need it anymore. It has the same functionality as in destructor method of an object in C++. When an object supports multiple interfaces, a client needs to get the interface pointer which is required. QueryInterface() method provides the necessary navigational functionality. A client knows the interface identification number (IID) and calls QueryInterface() function so that the server object can decide which pointer it should return.
Since an interface is a pointer to a table of function addresses, any given function is identified with three elements:
A class factory is a component to create an instance of one group component. An implementation of class factory object should support IClassFactory interface. IClassFactory interface has two functions except the functions in the IUnknown interface:
This function tells component whether it should deallocate itself as soon as its reference count drops to zero. This functionality is required for performance reason when a client needs to define several instances of component served by the class factory. Then client lock the class factory object until it is done with it.
First parameter sends an interface pointer to possible outer interface when aggregation is used. Component decides whether it allows itself to be aggregated or not. If it does not and this parameter is not NULL, then it should return an error code (CLASS_E_NOAGGREGATION). returnInterfacePtr returns the pointer to the instance of the required component.
A class factory implementation also implements the IUnknown interface. This object returns a pointer to itself when QueryInterface() function is called for IUnknown or IClassFactory interface.
Component developer provides implementation of class factory object. The advantages of this approach can be summarized as the following:
Components can be defined in two different formats:
DLL describes any shared library and components can be defined within DLL format. DLLs can be loaded into and executed within a client's process. COM can load the library that contains the component if the DLL is not already loaded.
Components can be defined in stand alone executable and COM can start them whenever it needs.
A client and a server object can be position themselves in two ways:
The DLL containing the implementation of the required components is loaded into the client's address space and all function calls to the interfaces can be done directly without marshaling and unmarshaling. Therefore, this settings is the most performance efficient way to make a function call between a client and a server object.
The DLL or EXE containing the necessary definitions leave in another process and COM introduces a proxy/stub for communication between a client and a server object. Since the client never knows where exactly the server is running, location transparency is secured. But, this setting involves marshaling and unmarshaling, therefore, it takes more time than the in-process settings for each function call. Out-of-process servers can be running on the same machine where the client is, or on a remote machine.
EXE component definitions can be used as an out-of-process server object. DLLs can be loaded into client's address space to be used as an in-process server object or can be loaded by a surrogate process (remote DLL) and it can be used as an out-of-process server object. When a server object sits in another process, then the communication between a client and a server is done with the help of proxy/stub pair. A proxy object is created in the client's address space and it acts like a real server object. Actually, a proxy forwards all function calls to the stub object leaving in the server process. When the stub receives a request from the proxy, it executes the function call on the server object, and returns the parameters back to the proxy object. The messages between the proxy object and the stub are coded according to Object Remote Process Call (ORPC). This format defines a way to code function calls and parameters of function. This coding is known as marshaling. Therefore, the proxy object marshals the function call and sends this package to the stub object and the stub object unmarshals this package.
COM Library provides a set of functions to ease the development of COM components. First step to use COM is to start it, and when there is no need any COM service, stop it.The following functions provide the necessary functionality:
After the COM library is initialized, now it is time to obtain a pointer to the object which implements a useful interface. CoCreateInstance() function is used in general even though there are several other ways.
CoCreateInstance | ( REFCLSID requiredCLSID, IUnknown* outerInterfacePtr, |
DWORD class_context, REFIID requiredInterfaceID, void** returnInterfacePtr) |
CoCreateInstance takes CLSID, creates an instance of the corresponding component and returns an interface pointer to this instance of the component. requiredCLSID denotes the class identifier of the required component. COM searches HKEY_CLASSES_ROOT\CLSID tree in the Registry to find out a match for the requested component. If COM finds a valid registered implementation for this class then
COM requires that registered CLSID implementations have to support IClassFactory interface as COM relies on the existence of the class factory to instantiate a pointer to the required interface.
Third parameter in the CoCreateInstance call specifies where the component resides:
CLSCTX_INPROC_SERVER | : | Component must be implemented in DLLs to run in the client's process. |
CLSCTX_INPROC_HANDLER | : | An in-proc handler is an in-process component that implements only a part of a component. The other parts of the component are implemented by out-of-process component on a local or remote server. |
CLSCTX_LOCAL_SERVER | : | Local servers are implemented as EXEs and they have different process than the client has. |
CLSCTX_REMOTE_SERVER | : | On remote machine and needs DCOM to work. |
CoGetClassObject() function is used to get the pointer to the class factory of the object and then CreateInstance() function can be used to obtain the pointer to the required interface.
CoGetClassObject | ( REFCLSID requiredCLSID, DWORD class_context, |
COMSERVERINFO *pServerInfo, REFIID requiredInterfaceID, void** returnInterfacePtr) |
Returns the class factory object related to given class identifier (CLSID). class_context parameter has the exact same meaning in CoCreateInstance() function. requiredInterfaceID parameter contains the interface identifier (IID) of class factory object of the class identified by requiredCLSID. returnInterfacePtr parameter contains a pointer to the class factory object if the call succeeds. Then, CreateInstance() function can be used on this interface to obtain the pointer to the required interface. CoCreateInstance() function actually uses CoGetClassObject() function to obtain a pointer to the class factory interface, and calls CreateInstance() to obtain the pointer to the required interface. CoGetClassObject() function can be used directly by clients if a client needs several instances of component by the client. Because, CoCreateInstance() gets the interface pointer of IClassFactory object, asks the necessary interface to it and releases IClassFactory object for each invocation. This definitely provides a performance improvement for a client.
COM uses WIN32 functions for loading and unloading DLL definitions from the memory so that it can utilize the memory usage and remove the DLLs that are no longer required. DLLs share the same address space as the application they are linked to. Ofcourse, COM requires that DLLs should provide some set of standard functions so that COM can work with them. Before, we look at those functions, let's look at the functions that COM uses to provide lifecycle support for DLLs.
LoadLibrary | () |
Loads a DLL into caller's address space.
FreeLibrary | () |
It decrements the internal Windows reference counter for this DLL and unloads it if it drops to zero.
GetProcAddress | () |
Retrieves pointers to the DLL's exported functions
DEF file tells which function will be in DLL interface. LIBRARY keyword gives the name of DLL file and EXPORTS section lists the name of the functions and their function indices. Note that those functions should be decorated with "extern 'C' " if they are written in C++. When components are defined in DLL, DLL has to provide the following functions:
DllCanUnloadNow | () |
DllCanUnloadNow() function is used for asking DLL whether it is safe to unload this DLL from the memory, therefore, DLLs are supposed to keep track of their supporting components. This function is being called by COM in CoFreeUnusedLibraries() function that is called by COM during its idle time and CoUnitialize() function.
DllGetClassObject | ( REFCLSID requiredClassID, REFID requiredInterfaceID, void** returnedInterfacePtr) |
DllGetClassObject() is the first entry point to get the pointer to the IUnknown interface of a component. DLL checks whether it supports the required component (CLSID) or not. If not, returns an error (CLASS_E_CLASSNOTAVAILABLE). It instantiates a class factory object, asks a pointer to IClassFactory interface, and returns this pointer in returnedInterfacePtr. IClassFactory interface is not sufficient when custom activation is required such as licensing to restrict the component to be used on a properly installed machine, therefore, this can be handled in DllGetClassObject() function by calling proper functions on the interface before creating the IClassFactory object of the component.
The name of Dynamic Link Library is obtained from the Registry. This process requires that the DLL should be registered before through some tools or it should support self registration of the components that DLL includes. COM expects the DLLs supporting self registration to implement the following functions:
DllRegisterServer | () |
COM calls this function on the DLL to let it register the components that DLL provides the implementation.
DllUnregisterServer | () |
COM calls this function on the DLL to let it unregister the components that DLL registered before.
DllMain | () |
We can summarize in detail what the COM is doing to obtain a pointer to the required interface as follows:
CoGetClassObject() function uses the Service Control Manager (SCM) to locate and load the component, therefore, the first three steps are done in CoGetClassObject() function.
Serving component from an EXE is different from serving components from DLL. First, EXE components can not export their functions, therefore, a command-line options are used for EXE components. Second, EXE components have different process than their clients so that they require marshaling code between client and server. This leads to performance degradation for each function call compared to in-process servers. Meanwhile, this separation poses a less security thread to the client since the server will run in different identity.
It is responsibility of EXE component to register itself and corresponding class factory according to command-line parameters (-RegServer, -UnregServer, and -Embedding). COM Library executes the EXE component with -RegServer option when COM requires that it should register its components. Similarly, COM Library executes the EXE component with -UnregServer option when COM requires that it should unregister the components registered before. EXE components require a main or WinMain function.
Executable components are registered under LocalServer32 subkey. Note that it is possible to register LocalServer32 and InprocServer32 subkeys for the same component. COM decides which subkey to use according to the class context parameter in CoInitialize or CoGetClassObject functions. If the class context is CLSCTX_LOCAL_SERVER, then COM uses LocalServer32. If the class context is CLSCTX_INPROC_SERVER, then COM uses InprocServer32.
COM uses -Embedding flag to inform component that it is being launched because of client's activation request. Component should register its class factory object and call CoInitializeEx function.
CoInitializeEx | () |
The second parameter gives the threading model that the component wants to use.
EXE components use CoRegisterClassObject function in COM Library to register an object and its class factory.
CoRegisterClassObject | () |
After the registration, this process should stay alive to be able serve the client's requests. Therefore, WIN32 event mechanism can be used for this purpose and component can wait until it receives a certain event (WaitForSingleObject()). CreateEvent function in WIN32 library can be used to create an event and class factory object can create this event as soon as the interface realizes that there is no need to stay in the memory. EXE component should unregister the class factory object by using CoRevokeClassObject() function in COM Library.
CoRevokeClassObject | () |
EXE component should inform the COM that it is done with its services by calling CoUninitialize() function. Since EXE components are out-of-process servers, they require marshaling code. Marshaling can be done several ways such as standard, type library, and custom (See 2.2 Remote DLL).
Interface definion languages (IDL) are used for describing interfaces. Microsoft IDL is an interface definition language based on DCE IDL specification. MIDL also introduces additional keywords which are COM specific. Those keywords can be summarized as follows:
MIDL compiler produces necessary header and self registering DLL files which implements Proxy/Stub pairs in DLL format. Interfaces should be registered HKEY_CLASSES_ROOT\Interface tree. A record related to IID contains ProxyStubClsid32 subkey which refers the CLSID of the proxy/stub DLL for this interface. This CLSID value is searched under HKEY_CLASSES_ROOT\CLSID tree and the entry related to this CLSID contains InprocServer32 subkey. The value of InprocServer32 gives the name of the proxy/stub DLL.
Components can be defined within type libraries (.TLP) that provide type information about interfaces, methods, properties, arguments and structures in the defined components. A type library is compiled version (binary) of an IDL file that can be accessed programmatically such as Visual Basic and Microsoft Visual J++. Automation library provides standard components for creating and reading this binary file. This feature allows Visual Basic clients to access functions through v-table so that function calls can be made faster because there is no need for marshaling.
MIDL introduces library and coclass keyword in the definition so that MIDL compiler produces a type library files for this components and all interfaces (defined with coclass) that are contained by this component. Anything defined in the library section of IDL file is added to the type library and they are identified with type library identification number (LIBID). Type Libraries can be registered with LoadTypeLibExe utility under HKEY_CLASSES_ROOT\TypeLib tree. Registration of type library can be done programatically by using CreateTypeLib2 function. COM defines ICreateTypeLib and ICreateTypeInfo interfaces for building type libraries and ITypeLib and ITypeInfo interfaces for reading them. CreateTypeLib2 function in COM library is called to create a type library and obtain a pointer to the ICreateTypeLib2 interface. ICreateTypeLib interface has the following functions:
Type libraries may contain help strings for all the components, interfaces, functions inside the library and this makes easier for object browsers (such as Visual Basic) to provide help for those components. Visual C++, Visual J++, and Visual Basic are capable of reading type libraries.
An instance of an COM object is created by providing CLSID to CoCreateInstance() function. COM uses a special object called The Service Control Manager (SCM) that provides a server object when a client request is made. SCM is a part of COM library and responsible for locating server objects. Therefore, SCM keeps a database for class information which is extracted from the registry. When a client requests to create an instance of class determined by its CLSID, the COM Library passes this request to the local SCM and expects that SCM will return a class factory object. SCM takes actions according to the type of registered server object:
In-Process: It returns the file path of .DLL. Then The COM library loads this DLL (LoadLibrary()) and gets the pointer to the class factory object (DllGetClassObject()).
Local out-of-process server: It starts the registered executable so that this server registers its class factory and the COM library can pick it up from the registry.
Remote out-of-process server: It contacts the SCM on another machine and forwards this request. The remote SCM starts a server, defines a connection to this class factory object, and returns an RPC connection corresponding the remote class factory. The local SCM creates a proxy for this remote object and returns this object to the COM Library. This proxy object forwards all calls to the real object running on another machine.
COM supports on-demand server object initialization. There is no need for running the object and keep it running to make it available to prospective clients. COM with the help of Registry can start/load the necessary server implementations.
COM supports Location Transparency so that an object can be in the client's address space, or in another container. If the server object is in another machine (its entry in the COM registry reveals this), then COM starts a proxy server and this talks to Service Control Manager (SCM) of the other machine and starts the necessary object on that machine and returns the end-point address for communication with the object on the remote machine. Client receives the pointer of the proxy object running on the local machine. COM places its own remote procedure call (RPC) infrastructure code into the v-table and then packages each method call into a standard buffer representation (Proxy), which it sends to the component's side, unpacks it, and reissues the original method call on a server object (Stub). While COM internals struggle with these processes, developer never sees a difference in the client/server code.
Versioning in COM is provided by defining additional interfaces for the new server object. Clients has to take care of controlling (QueryInterface()) whether server supports particular interfaces or not as objects can support multiple interfaces.
COM supports persistent object stores for the components which provides IPersistStorage interface which defines a protocol to restore/store objects.
COM relies on the reference counting technique for garbage collection. In this framework, COM component keeps track of active number of references to each interface and controls its lifecycle.
AddRef() and Release() functions in the base IUnknown interface implement necessary reference counts. COM component developer keeps a counter for each interface and increments this counter whenever AddRef() function is invoked and decrements this counter whenever Release() function is called. Also, in Release() function implementation, it checks whether the counter is dropped to zero or not. If it is zero, then it deallocates the instance from memory. For thread safety InterlockedIncrement() and InterlockedDecrement() functions should be used. This concept also puts several responsibility onto the client of this component. Therefore, there are some rule of thumbs which needs to be followed by the COM clients:
In COM, reference counting should be done per interface since:
This complicated rules can be easily violated by the client during development phase. The price is that interfaces never being removed from memory and performance loss. Also, there are some cases that client actually do not need to call AddRef() and Release() pair for copy operations. When it is guaranteed that the life-cycle of the copied reference is nested in the life-cycle of the source of this copy operation.
There are also another set of rules when interface pointers are used as a function parameter. The following rules should be followed:
Sometimes even very careful developers can miss one of the cases and it might turn out to be a nightmare to debug it and find where this pair of calls are missed. Therefore, a smart pointer concept is proposed as a solution for this problem. A smart pointer is a class that overwrites operator-> The smart pointer class contains a pointer to another object. Similarly, a smart interface pointer is a smart pointer that contains a pointer to an interface. Smart interface pointers are useful especially dealing with reference counting problems in COM component development. This wrappers invokes the necessary AddRef() and Release() pair of calls for a client.
Note that figuring out whether client should call AddRef() on the interface pointer coming from a function is completely problematic especially the developer of this component did not stick to these common rules.
As a COM component developer, you might want to decompose your problem space and define several sub component which will carry out the some specific tasks instead of putting all the functionality into one component. There are two ways to obtain this: containment and aggregation.
In containment, a component keeps a pointer to other components and implements all their interfaces by directly calling the same functions or addition of new codes before/after the function call. Containment based approach never let outsider see the contained components.
Aggregation means that an object contains several interfaces and implements them through several objects. Aggregation based specialization allows client to access the contained component and invoke the function call directly on them. Microsoft recommends developers to use aggregation for components which are quite substantial and composed of a lot of interfaces. This has several advantages in terms of performance and memory usage:
If the server object has a lot of services, it can be divided into sub-interfaces and each interface can be handled by a different object. Then a developer defines a wrapper object which contains all interfaces. It takes less time and less memory when COM starts the wrapper object. Whenever one interface is asked, the related object is being brought to memory instead of the whole component.
This type of aggregation makes it possible to distribute subcomponents onto different machines so that load sharing can be achieved with no cost.
Unfortunately, a simply returning the pointer of included component is not enough since this reference does not point the outer component's interface. Therefore, a component should be returning the outer components IUnknown interface if it is aggregated in.
For solution of this problem, each aggregatable interface provides two types of IUnknown interface one for delegating and one for non-delegating IUnknown interface. Non-delegating interface definition provides all the base IUnknown functions while delegating interface has an IUnknown pointer to some interface and it forwards all the calls to this interface. If the component is aggregated in another component then this pointer points the outer component's interface. If component is not aggregated in, then this pointer will point to non-delegating interface. Outer component makes all function calls on non-delegating interface. Note that the delegating interface should be extended from non-delegating interface also. This will guarantee that a client can not get the pointer to an aggregated component.
This requires two version of CoCreateInstance functions one for non-aggrageted and one for aggregated version.
DelegatingIUnknown interface keeps a pointer to the outer IUnknown interface. If component is not aggregated within another component, this pointer references the NondelegatingIUnknown interface so that it can work without any problem. DelegatingIUnknown interface forwards all the calls to this referenced interface. This will also require some changes in the CreateInstance function of IClassFactory interface and the constructor of the aggregated interface to take care of possible aggregated use.
Automation focuses on run-time type checking at the expense of speed and compile-time type checking. Automation replaces code generated by the compiler with code written by developer. Automation Server is a COM component that implements the IDispatch interface. Automation Controller is a COM client that communicates with the Automation Server through its IDispatch interface. Automation Controller uses member functions in the IDispatch interface to indirectly call functions in the Automation server. Automation controller does not need the interface definition. IDispatch interface supports the following functions:
The set of functions implemented by an IDispatch::Invoke() implementation is called a dispatch interface (dispinterface). The implementation part requires to implement a table for function name-to-DISPID and DISPID-to-Function pointer transformations. For small number of functions, a vector can be used while for large number of functions hashtable will be preferable.
Dynamic invocation utility degrades the performance of function call for C++ users since they rely on v-table entries. Dual interface concept is proposed to solve this problem. An interface is extended from IDispatch interface instead of IUnknown interface after all IDispatch is extended from IUnknown, therefore it is perfectly alright with COM internals. This solution allows clients to access methods of an interface through v-table or through Invoke() function, therefore they are called dual interfaces. Dual interfaces are preferred way of implementation strategy since it allows C++ clients directly use functions through v-table while automation clients use dispinterface.
This functionality makes Visual Basic Developer's life very easy while C++ Developer's life turns out to be a suffering. For a C++ Programmer Client:
The types of parameters that Invoke() can pass to a function in a dispinterface are limited in number(VARIANTS).
Giving a which function, client wants to call is a problem because each name there are four possible cases:
These functions come from the MIDL definition for providing easy method definitions for accessor methods(get_XX,put_XXX). Therefore, Visual Basic User uses only names in the program and interpreter executes an appropriate method on an interface.
Dispatch parameter structure supports Name-value paired parameter list. For C++ programmer, name values makes no sense, providing parameter values is sufficient.
Results are obtained as a VARIANT array. Exceptions are obtained as an EXCEPTINFO structure.
VBScript and JScript clients require IDispatch interface of components. Visual Basic checks whether component supports dual interface or not. If it finds out that it does and there is a type library defining this interface, then it directly uses the custom interface(v-table) instead of the dynamic invocation.
Monikers allow COM to name objects and activate them in the system, therefore it is also known as intelligent names. The requirement for Monikers becomes clear since CoGetClassObject and CoCreateInstance functions in the COM library is not enough to define a constructor with parameters. A custom activation interface can be defined for this object but Visual Basic client unfortunately can not work with this component. Monikers enable all objects to deal with naming through a single, standard interface (IMoniker). The COM library contains several default moniker implementation and functions to create them and all of them accepts additional arguments:
BindMoniker function in the COM Library takes an IMoniker interface pointer and binds that moniker to the object it names. This function defines a binding context(IBindCtx) by CreateBindCtx function, calls BinToObject function in IMoniker, and returns the pointer to the interface.
Since each moniker defines a relationship between the name and the object,
this name can be displayed and called as display name. For example, for
file moniker, obviously the display name is the full pathname of the wrapped
file. For the class moniker, the display name is the 'clsid:
MyParseDisplayName function in the COM Library converts a string to a moniker that identifies the object named by given string. MkParseDisplayName function returns a corresponding file moniker object if the string contains a full path name of a file. If the string has 'ProgId:ObjectName' format, ProgId is a registered program identifier, then function gets the CLSID by using CLSIDFromProgID function. Then, function instantiates the moniker (CoGetClassObject), gets the IParseDisplayName interface pointer (QueryInterface), executes ParseDisplayName function so that a moniker identifies this name is defined.
GetObject function in Visual Basic and CoGetObject function in C++ handles this mess.
The class moniker is a system moniker defined in the COM Library. The class moniker wraps the CLSID of a COM class. The class monikers can be used with MkParseDisplayName to refer an object with this CLSID and activate it (BinTToObject). In the registry, HKEY_CLASSES_ROOT\CLSID\clsid contains the class id of the class moniker object. Once a pointer to the class moniker is obtained, then a BindToObject call on this moniker returns the required interface pointer. The class moniker offers Visual Basic clients an access to custom activation interface.
Structured Storage service in COM enable an application to save data in a single file in a standardized, structured format. Structured storage format is a hierarchical format and can include some storages and each storage can contain substorages and streams(like files). IStorage and IStream interfaces are defined to support this service in the COM.
Connectable objects allow server objects to talk back to its clients. An outgoing interface(source interface) is an interface defined in the object itself but implemented by the client. A sink object, residing within the client, is the object that implements the client's outgoing interface.
IConnectionPointContainer is the first interface pointer which allows client to obtain the pointer of IConnectionPoint interface. It has the following functions:
IConnectionPoint interface has the following functions which allows client to query the IConnectionPointContainer interface in which this connection point is managed, the IID of sink interface which is supported by this connection point and information about current connections.
GetConnectionInterface() : returns the IID of the outgoing interface managed by the connection point.
GetConnectionPointContainer() : returns a pointer to the IConnectionPointContainer interface associated with the connection point.
EnumConnections() : returns a pointer to the IEnumConnections interface, enabling a client to enumerate all the connections that exist on a connection point. IEnumConnections object keeps the CONNECTDATA elements. CONNECTDATA is a structure which has a pointer to the sink interface and its associated cookie information.
source keyword in MIDL indicates that the defined interface is a source of events and that the interface is called and won't be implemented by the server.
A connection point is an object managed by the connectable object that implements the IConnectionPoint interface.
Advise method is called by the client in order to provide the connectable object with a pointer to the sink object. This method must return a unique number to the client that identifies the advisory relationship that has been established. This number is called a "cookie" and is used for terminating the connection between client and server object.
The Unadvise method is used to terminate an advisory relationship that was previously established using the Advice method.
Client gets the pointer to server object, queries the IConnectionPointContainer interface pointer, looks at whether the server supports the registration of certain sink interface which client has, gets the pointer to IConnectionPoint interface and calls Advise on this interface by providing the pointer of its sink object. Keeps the cookie coming from as a result of Advise method and when it no longer wants to receive those callbacks, client invokes Unadvise method on the IConnectionPoint interface and releases all the interface pointers that client collected from this process. These four round trip is necessary for the first connection set-up and sometimes it might not be desirable especially in remote out-of-process setup because the cost involved.
COM threading model sits on top of WIN32 threading model. A thread is schedulable entity owned by one and only one process and the operating system recognizes each thread in every process and is responsible for scheduling them to the system processor(s). Each thread owns its call stack. Windows thread scheduler preemtive and time sliced with priorities.
Microsoft Windows defines a message loop to monitor incoming messages sent by the operating system. If application has a user interface, all events are also scheduled to this message queue. The primary thread pulls this message off the queue associated with the window. According to the programming languages, this messages should be pulled (in C and C++) or translated to events as in Visual Basic.
CreateThread function in WIN32 API allows to define a new thread. WIN32 API provides critical sections, mutexes, semaphores, and evenets for synchronization. WIN32 API allows thread to have persistent data storage are called thread-local storage (TLS). This area can be used for storing local variables pertaining to this thread while this area can not be accessible from another thread.
Since COM is a binary standard different threading models can not be mixed to each other especially for in-process servers. Therefore COM has to take care of this problems.
Apartment concept is emerged beceause of backward compatibility. There is no way to recompile every client application with the same threading model. Idea behind Apartment concept is to define an environment for components such a way that they can interoperate without any change in their thread policy. A thread in one apartment can not directly access a function of an object leving in another apartment. Inter-apartment communications pass through proxy/stub objects so that these calls are serialized. Free-threaded refers to MTA. An apartment threaded refers to STA model. Visual basic can not create components in MTA. C++ can directly use WIN32 API. Java provides a better develpoment environment since it has already have the language level constructs for concurrency control.
COM creates a window message queue for each STA. When RPC layer in STA receives an incoming message. it posts thie message (PostMessage function in WIN32 API) to the message queue. A (primary) thread in STA process messages from this queue acdcording to First-In-First-Out (FIFO) strategy. This level of serialization solves all concurrency problems. If the client's running thread modeil is different than the thread model of the in-process component, then COM either creates or uses another compoatible apartment to run this component.
CLSID\InprocServer32 can have a ThreadingModel attribute denoting the threading model of the component.
If there is no ThreadingModel attribute COM assumes that it is Apartment.
RunAs Launching user causes the creation of the multiple copies of the same server. RunAs interactive user also causes problems in production mode since the security principal is going to change according to the current user logged in the server machine. If there is no one logged in. then that component won't be available at all. It only makes sense to use in development phase. The cleanest choice is to define one user identity for these server components and use the same identity for each server object as a security principal. Adiministration will be easier since we are only gonna deal with one user and its credentials.
COM defines several levels of thread safety called threading models. The basic unit of thread safety in COM is called an apartment. There are two kinds of apartments: single threaded apartments (STA) and multi threaded apartments (MTA). STA have only one thread per apartment while MTA can have more than one thread per apartment. A process may contain any number of STAs while it can contain at most one MTA.
Component process declares its threading model at start-up and COM handles the rest of concurrency issues. The second parameter of CoInitialize and CoInitializeEx functions indicates the preferred thread model: COINIT_APARTMENTTHREADED for STA and COINIT_MULTITHREADED for MTA.
Method calls on object running in STA are dispatched using window message queues for synchronization so that multiple clients can make concurrent calls to an object running in STA. In the STA model, an interface pointer returned with CoCreateInstance function can be used only in the same STA. If the transfer of this pointer is required, COM provides two functions: one for marshaling the pointer into a stream (CoMarshalerInterThreadInterfaceInStream) and one for unmarshaling the pointer from the stream (CoGetInterfaceAndReleaseStream). In the MTA model, function calls are done directly through v-tbl therefore developer of component should provide the necessary synchronization mechanism for critical sections. Interface pointers can be passed directly between threads in the same MTA but they require marshaling when thread safety unit boundaries are crossed.
Security is handled based on roles in the Windows NT system. Windows NT maintains an access token for each user logged on to the system, containing information identifying the user and the user groups to which the user belongs and information about other privileges. Each process started by user also keeps the copy of this access token. The operating systems compares the access token of the running process with the security information attached to the system object that process wants to use to decide grand the permission or not.
Each user and user group has an unique security identifier (SID) in the access token. Each system object keeps a security descriptor containing the SIDs of the owner and the primary group to which owner belongs. Security descriptor also contains the discretionary access control list (DACL) and system access control list (SACL). DACL controls the access permissions while SACL lists the object's operations that should generate audit messages. Elements in lists is called access control entry (ACE) and each ACE contains a grant or deny information for a specific user or user group.
COM provides a declarative security support and programmatic security support. Declarative security support allows to define security for per system or per component basis with the help of system tools. Programmatic security support requires implementation of several security interfaces to enforce more restricted security needs or selective security on the interface.
COM security aims to control activation, access, authentication, and identity. Activation control defines who has a permission to launch a component. Access control specifies who has a permission to access the component's objects. Authentication control ensures that a network transmission is authentic and the data is protected against unauthorized viewers. Identity control defines the security credentials under which the component executes.
Declarative security can configure all controls and store security information in the registry. Access and authentication control can be done with programmatically while activation and identity control can not since these controls are applied before the execution of the component. COM abstracted the security needs of components with Security Support Provider Interface (SSPI) API so that it is not tightly coupled with any operating system.
Using registry to keep the security information lets COM automatically enforce all the requirements. Even though it sounds like it is vulnerable model to keep all this information one place, COM only allows privileged users to access the registry for update and run the utility programs related to registry. Declarative security allows to define a default security for all components as well as a security for each individual component. A security description for individual component overwrites the default security settings.
There are seven authentication levels defined by COM:
Component's view of a client in security context is defined by Impersonation Level. COM supports the following impersonation levels:
Note that Anonymous is not supported in COM yet and Delegate option can be used in Windows NT 5.0. Default Impersonation Level and Default Authorization Level settings can be done with DCOM Configuration utility and applies to all users of the system.
Security description for each component is stored under AppId entries in the registry. Four subkeys (LaunchPermission, AccessPermission, AuthenticationLevel, and RunAs) are related to security definition of the component. These four subkeys controls all security items.
IAccessControl interface is the part of SSPI API for portable security support of COM.
Programmatic security allows a finer control on the security issues related to a component since the developer has all the control. CoInitalizeSecurity function in COM sets the security descriptions for the current process and it should be run after CoInitialize() function to make sure that COM does not start a default security description. COM executes this function the first time an interface pointer is marshaled into or out of an apartment in the process. CoInitalizeSecurity function allows developer to provide necessary security controls through parameters. The format of the function is:
Programmatic security is done through providing an implementation of IAccessControl interface. COM calls IsAccessAllowed method to perform access checking at run time so that developer can decide to grant the access or not for a particular trustee (a user, a user group, etc.). Since COM can invoke this function in any thread, the implementation should be thread-safe.
Server can enforce higher levels of security a per-method basis by implementing the IServerSecurity interface which is used to identify the client and impersonate the client's identity. IServerSecurity interface contains the following functions:
CoGetCallContext function in COM allow to obtain the pointer to the IServerSecurity interface. Similarly, CoQueryClientBlanket function can be used to do QueryBlanket call on the interface, CoImpersonateClient function can be called for ImpersoneClient call, and CoRevertToSelf function can be called for RevertToSelf function call on the implementation of IServerSecurity of an object.
Client side proxy uses IClientSecurity interface for security purposes. IClientSecurity interface contains the following functions:
As in IServerSecurity interface functions, COM provides several functions that directly calls the necessary functions on the proxy's IClientSecurity interface implementation. CoQueryProxyBlanket function calls QueryBlanket, CoSetProxyBlanket function calls SetBlanket, and CoCopyProxy function calls CopyProxy function on the IClientSecurity interface.
Registry is a hierarchy of elements whose each element is called a key. A key can include a set of subkeys, a set of named values, and/or one unnamed value. Subkeys can have other subkeys and values. COM uses HKEY_CLASSES_ROOT branch of Registry tree.
CLSID Key contains an externalized GUID of registered classes. Under this representation, it might have an InprocServer32 key with an appropriate DLL file name.
ProgID key is used to map a programmer friendly string to a CLSID so that users of these components can access components through ProgID values. The default value of ProgID denotes the program name, component name and version number. This should be a key in HKEY_CLASSES_ROOT tree, including CLSID of this component. ProgID might contain a key named VersionIndepenedentProgID whose value again a key in HKEY_CLASSES_ROOT tree.
HKEY_CLASSES_ROOT\Interface key contains information about each COM interface available on the system that has an interface identifier. For each interface, one IID subkey is required and this subkey might contain the following subkeys:
HKEY_CLASSES_ROOT\TypeLib defines each type library installed on the system. Every LIBID key in the registry contains several subkeys that specify the version number of the type library, the path to that library, several flags, and an optional path to a directory containing help files with information about the contents of the type library.
Active Template Library(ATL) (ATL) is composed of C++ template classes that provide support for the most common COM programming procedures as well as dealing with component registration issues.
DCOM is a prot ocol that enables software components to communicate directly over a network in a reliable, secure, and efficient manner. Microsoft's Distributed Component Object Model (DCOM) currently is available on two operating systems: Window95 and Windows NT4.0. DCOM is language-independent, and objects are described via their interfaces using Microsoft's Object Description Language(ODL). DCOM provides everything COM does and additionally it has the following features:
DCOM supports distributed object reference counting by Pinging protocol. Client machines send a periodic message to indicate that they are still using the object. DCOM considers a connection as broken if more then three ping periods pass without the component receiving a ping message. If the connection is broken, DCOM decrements the reference count and releases the component if the count becomes zero(no client).
DCOM's preferred transport protocol is the connectionless UDP subset of the TCP/IP protocol suite. DCOM takes advantage of connectionless protocol and merges many low level acknowledge messages with actual data and pinging messages.
Proxy handles the client requests and serves the client. But proxy can bundle the several request and keeps their results in its cache and serves the client from this cache. So that every client request might not need a network communication.
DCOM uses Microsoft's RPC protocol (barrowed from OSF's DCE) for remote method invocation.
Each component can have an Access Control List(ACL). When a client calls a method or creates an instance of a component, DCOM obtains the client's current username associated with the current process. Windows NT authenticates this information. On the server object, DCOM validates this information by looking at component's ACL.
Application developers can use the same security services to define a finer security restrictions on the object such as interface based or method based security.
Distributed components can be defined in DLL or EXE binaries. We are going to focus on the DLL in this section. DLL is a library description with certain hooks so that COM can extracts the information it needs from this library. When DLL is loaded within client's process, there is no problem with execution thread and security restrictions. When DLL needs to be loaded remotely (w.r.t. client's process), it requires an additional structure providing thread of execution as well as the security features. This problem is solved by surrogates: an executable responsible for loading a DLL, providing a thread of execution, and security features.
Default DLL surrogate (dllhost.exe) is already provided with DCOM distribution and it is generally sufficient for most of the DLLs. When a component runs in surrogate, it has to provide an AppId subkey under its CLSID registry entry. The value of AppId subkey is 128-bit GUID and COM looks for a match in HKEY_CLASSES_ROOT\AppId subtree of registry. An AppId registry entry may contain the following subkeys:
The value of DllSurrogate subkey denotes the surrogate object. If this subkey does not have a value, then COM uses the default DLL surrogate (dllhost.exe). An AppId entry groups the configuration and security options for distributed objects in the registry. Therefore, the same AppId entry can be shared by several objects.
As we know, client starts with CoCreateInstance() function to get the pointer to required interface. Third parameter of CoCreateInstance() function actually tells from where this object should be obtained. Let's assume that CLSCTX_LOCAL_SERVER is used as a third parameter. This defines rules out that the DLL which contains the component will run in the same process. As we recall, COM passes this request to the SCM and expects to get the pointer to the interface. SCM takes the following actions:
The object can be running on the local machine or a remote machine. An AppId entry can contain DllSurrogate and RemoteServerName subkeys at the same time. Client directs COM by using third argument of the CoCreateInstance function to use which subkey. Lets assume that AppId entry contains both subkeys. If client calls CoCreateInstance with CLSCTX_LOCAL_SERVER, SCM uses DllSurrogate subkey. If client calls CoCreateInstance with CLSCTX_REMOTE_SERVER, then SCM uses RemoteServerName subkey.
You might wonder why there is a DllSurrogate subkey definition at all if the default DLL surrogate can take care of DLLs. The answer is still you might need them especially for DLL codes that
These components are developed under the assumption that DLL and the client code are going to run in the same process. Unfortunately, when we need to run this component remotely, we will run into a lot of unexpected problems. Solution is to write custom surrogates for these components.
Custom surrogate should register itself with CoRegisterSurrogate call and provide a mechanism to terminate itself when there is no client for the components. This can be done through events. Surrogate registers itself and waits for an event and as soon as it detects this event, it terminates. Custom surrogate should supply an implementation for ISurrogate interface, because COM will asks a pointer to ISurrogate interface. ISurrogate interface contains two functions: LoadDllServer and FreeSurrogate. After SCM finds out that it needs to use a DllSurrogate, then it starts this executable and asks a pointer to ISurrogate interface (through DllGetClassObject). SCM calls LoadDllServer function on this interface and it directs all CreateIntance calls to this surrogate. An implementation of ISurrogate interface can use CoCreateInstance function to provide a pointer to the required interface. COM informs surrogate via FreeSurrogate function that there is no client using any component supported by this surrogate. Therefore, in this function, surrogate can create the event (or condition) that it was waiting for termination.
So far we described how surrogates work and load a DLL remotely. But, we did not tell how the pointer to required interface is transferred from one process to another. Since two processes have different memory layout (maybe on different machines), it is not possible to copy the pointer. This phase is called interface marshaling. As you recall, an interface pointer is a pointer to v-table structure. The same structure should be created in the client process. COM defines a proxy object in the client process for remote interfaces. Similarly, a stub is defined in the server side. Proxy behaves like a real interface and forwards all function calls to the stub on the server side. Stub object redirects these calls to the implementation of the interface and returns back the results to the proxy. Then, proxy returns these results to the client like it is running in-process.
COM needs the necessary code for proxy and stub implementation. Fortunately, MIDL compiler also produces implementation for proxy/stub DLL so that SCM locates the proxy/stub DLL with ProxyStubClsid32 subkey in the CLSID registry of the required class object. After SCM loads the DLL, then it asks a pointer to IPSFactoryBuffer interface via DllGetClassObject function.
IPSFactoryBuffer interface has two methods: CreateProxy and CreateStub. On the client side, CreateProxy function and on the server side CreateStub function is called. CreateProxy function creates an object which implements IRpcProxyBuffer interface as well as the necessary custom interface. CreateStub function creates an object that implements IRpcStubBuffer interface. CreateStub buffer gets a pointer to the server object and the interface identifier.
IRpcProxyBuffer interface is used by the proxy manager to talk to a proxy. IRpcProxyBuffer interface has two methods: Connect and Disconnect. Connect function accepts a pointer to IRpcChannelBuffer interface that provides the necessary communication channel between proxy and stub. Disconnect function is called to notify the proxy that it is not connected to the stub anymore. It should release the IRpcChannelBuffer interface pointer.
IRpcStubBuffer interface is loaded into the component's address space and used for communication with the stub by the stub manager. IRpcStubBuffer interface has the following functions:
IRpcChannelBuffer interface introduces an abstraction which transparently (on the same machine or not) handles necessary transmission of marshaled data between processes. IRpcChannelBuffer has the following functions:
Invoke function of IRpcStubBuffer is used on the stub when the call is arrived.
COM allows three kind of marshaling:
Proxy/Stub DLLs must be registered in related IID entry under ProxyStubClsid32 sub key.
CoMarshalInterface function queries IMarshal interface of the object. If it is not specified, then standard marshaler is used. If it is specified, then custom marshaling will be used. After COM finds that object wants to use custom marshaler, it asks the CLSID of the proxy object via GetUnmarshalClass function. COM asks the size of package marshaler wants to use via GetMarshalSizeMax function and allocates a space from memory. When COM needs to marshal the pointer to an interface, it calls MarshalInterface function to let the marshaler do its job. This buffer is sended back to the client side and this time COM uses UnmarshalInterface function call to let marshaler create a proxy object in the client's process. SCM can get the CLSID of the proxy object from this buffer and instantiates this object. Then it finds that this object has its own custom marshaling interface support. It handed it the buffer back to this marshaler via UnmarshalInterface method and let the proxy open a communication with the stub. If everything is alright, this method returns a pointer to the proxy object so that it can handle all calls related to the custom interface and forwards them to the stub.
DLL surrogate (dllhost.exe) is a default DCOM surrogate that provide security and thread of execution for any DLL. DLL surrogate object can be instructed to load any dll remotely.
EXE components does not need a surrogate object since they already have a process for themselves. EXE components can use several ways of marshaling. If standard marshaling is preferred, then proxy/stub DLL can be obtained by the help of MIDL compiler. Type library marshaling can be used by adding oleautomation attribute to the interface definition. oleautomation attribute indicates that the interface will only use Automation compatible types. They differ on the client side behavior. If type library marshaling is in use, then client machines do not need to know this marshalers since the automation marshaler is the facility in COM library. If proxy/stub DLLs are used, then all these DLLs should be registered on all possible client machines so that they can find the proper marshaling definitions. Client and component can integrate the proxy/stub code. This way there is no need for separate for marshaling code but update means that client and component sources should change since the previous clients are using the previous marshalers. In this setting, both client and component should register the marshaling parts with CoRegisterPSClsid() function. This function temporarily registers the marshaling code for this process so that COM can find the necessary marshaling code.
CoCreateInstanceEx() function is designed for DCOM to create an instance on the remote or local machine. CoCreateInstanceEx() function can request multiple pointers with a single call to utilize the involved network traffic.
DCOM supports interceptors with IChannelHook interface. Interceptors are an extra code which will run after reception of each PDU before this PDU is processed and before the PDU is put into a wire. The logical connection between client and server object is defined as channel, therefore this technique is called channel hooks in DCOM. As you recall, DCOM adds ORPCTHIS for request PDUs and ORPCTHAT for reply PDUs and both structure contain an extension fields. This extension field can be used to send some information from client to server as well as server side to client side by the help of channel hook mechanism. Extension field contains an array of extent fields which is uniquely identified with a GUID. To be able to use this field, we need to provide an implementation of IChannelHook interface. IChannelHook interface contains the following functions:
An implementation of IChannelHook interface is registered with CoRegisterChannelHook function in DCOM and loaded into the address space of any client and server process by instantiating it via CoCreateInitialize function.
DCOM network protocol is called Object RPC (ORPC) since DCOM made some changes on the header description of standard DCE RPC definition. DCOM still uses the authentication, authorization, and message integrity features of the DCE RPC protocol. ORPC defines how calls are made on remote objects and how object references are represented, transmitted, and maintained.
IRemoteActivation interface is an RPC interface implemented by SCM on each machine. It has only one method RemoteActivation that activates the object on remote machine. RemoteActivation method returns the interface pointer identifier (IPID) and object exported identifier (OXID) for the requested object. IPID identifies the interface of an object running in a component and OXID identifies the RPC string binding information needed to connect to the interface specified by an IPID.
SCM runs on a specified well known endpoints for communication such as 135 for UDP and TCP/IP. Function calls are transferred through DCE RPC Protocol Data Units. PDUs contain the in parameters from client to server and out parameters from server to client.
DCOM uses connectionless UDP in default but it provides reliability because RPC ensures robustness by using a customized mechanism for message ordering and acknowledgment.
DCOM adds an ORPCTHIS structures as first parameter to each request PDU and ORPCTHAT structure as first parameter to each reply PDU.
SCM contains an object implementing IOXIDResolver interface. This interface stores the RPC string bindings to connect with remote objects, sends ping messages to remote object as long as there are client's for this remote object in the local machine, and receives ping messages for object running on the local machine.
OXIDResolver caches the mapping from OXID to the RPC bindings for future uses so that it does not need to do the resolution everytime. OXIDResolvers are used for distributed garbage collection. Each OXIDResolver running on each machine sends only one ping message to machines on which there is a server object that is in use by a local client object. OXIDResolver tries to aggregate the ping messages into one message. For huge number of relationship between client-server structure, the size of the message can be very large. Therefore, OXIDResolver also supports delta pinging technique where OXIDs are collected into a ping sets and only a ping messages are sent for those ping sets.
DCOM releases the object pointer whenever objects misses three times ping message. DCOM assumes 120 secs between consecutive ping messages so that each object can stay alive at least 6 minutes without receiving a ping message. After that, it can be terminated as soon as OXIDResolver decides to collect garbages.
Pinging is necessary if server object needs to keep a state information. But if the server object is stateless such as time server, then the server does not care whether client is alive or not and therefore there is no need for pinging. Component developer can direct DCOM by using MSHFLAGS_NOPING flag in CoGetStandardMarshal function call.
IRemUnkown is a DCOM interface that handles reference counting and interface pointer querying for remote objects. IRemUnknown interface contains the following functions:
DCOM defines an IRemUnknown interface implementation for each OXID object in each apartment. The IRemUnkown interface is designed to minimize the network traffic as much as possible. Therefore, RemQueryInterface function can return several interface pointers in one request. DCOM also allows a context definition for each client so that reference counting operation can be done for each client separately.
COM+ is the follower of COM and DCOM technologies with better development and technology supports. COM+ will provide a framework which will allow developers to build transaction-based distributed applications from COM components that can be managed from a single point of administration. COM+ will include
All COM+ components are COM components. In terms of development approach, COM+ eases a lot of complicated steps when we compared with its ancestors. For C++ development environment, it is worth to mention the following improvements:
MTS defines a set of COM interfaces for developing distributed components and an infrastructure for deploying and administering three-tier solutions. MTS provides a dll surrogate (mtx.exe) along with the MTS Executive (mtxex.exe). Exe-based components are not supported because they cannot be loaded into the address space of MTS. MTS requires the following features from the component to give its services:
MTS supports an just-in-time activation by late object creation and early object deactivation. MTS tries to utilize the resources of server so that servers can be more scalable than it would be. The real activation object is delayed until client invokes the first function call on the interface. Similarly, the deactivation of object does not wait until the reference counts drops to zero, it can deallocate the object while some client still has the valid reference to this object. In a case a client wants to us ethe same interface again then MTS recreates the copy of the object and client never knows which object is in use. Ofcourse, this magic requires some additional wrappers.
IObjectControl interface aims to provide just-in-time activation with the following functions:
MTS queries the IObjectControl interface when it instantiates the object. If this interface is supported, then MTS can use Activate function before the instantiation of the object and Deactivate function before the deactivation of the object. CanBePooled function returns whether an object is recyclable. If object can recycled, then this means that this object can be used by another client who would like to use an object to this same interface. MTS tries to address the recycling issue by introducing a distinction between the state of the object and the its code (behavior). MTS provides several hooks to save the state information for later reincarnation of the object.
The state information can be kept in:
MTS defines an implementation of IObjectContext interface for each running object in the server. IObjectContext interface contains CreateInstance function and extends from IUnknown interface. GetObjectContext function in MTS gets the pointer to the IObjectContext interface if object defines this interface, then MTS does not need to provide a default one. Under the control of MTS, instantiation of an object should be done with CreateInstance function of IObjectContext interface instead of CoCreateInstance function since an object created from IObjectContext interface can inherit the activity, transaction, and security identities from the creator's context object.
MTS uses the Microsoft Distributed Transaction Coordinator (MS DTC) which is originally developed for Microsoft SQL Server. MS DTC supports two-phase commit protocol and provides an implementation for four interfaces: ITransaction, ITransactionDispenser, ITransactionOptions, and ITransactionOutcomeEvents.
MTS acts as a transaction processing monitor for online transaction processing (OLTP). MTS Explorer is the main configuration utility function. mtx.exe is the surrogate process. Componetns should be defined in DLLs. Configuration information of components and packages running on MTS stored in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Transaction Server tree. All MTS components are distributed in packages that is a named unit of deployment. Each package is assigned a unique GUID and a profile in the catalog that contains a set of properties. A server package provides a surrogate process for running MTS objects. For each package, mtx.exe process is created. A library package are loaded into the cllient's address space. A library package provides a support for sharing component code code.
MTS allows only a single instance of any package to run on a machine at a time therefore it does not allow launching user as a security principal. Each MTS componet has associated class identifier. MTS Explorer changes the Registry for each registered component to format mtx.exe. If you look at the LocalServer32 subkey, it look like
\System32\mtx.exe /p:{'package-identifier-number'}
If the package is not running, then an executable server will be launched.
MTS Executive component is the main controller object in MTS. MTS Executive intercepts the request coming from SCM to create a new object. MTS Executive defines a wrapper object called context wrapper to monitor all the incoming and outgoing calls from object. call-by-call basis tapping.
Context wrapper places itself between the object and the stub. MTS fully takes advantage of interceptor concept since context wrapper intercepts before every function call. Context wrapper object stores the state information about object and its creator (security credentials).
MTS creates an object and defines a context wrapper for it. And it actually activates the object when the first function call is made on the object. Object has four state: creation, activation, deactivation, and destruction.
ObjectControl interface is used by MTS to inform the server object about important state changes of the object. MTS defines a new concurrency abstraction called an activity. An activity is a logical thread of execution created on behalf of a single base client. Activity represent a set of objects that belong to a single user. MTS creates a new STA for each new activity until the STA pool reaches its limit (100). After the limit, MTS starts starting multiple activities to each STA.
Online transaction processing (OLTP) systems refers to an application that notify data and have a large number of concurrent users. For distributed transaction management, transaction manager with two phase commit is used. Each computer runs Transaction Manager (TM). The application controlling the transaction is called transactional application. Each data store is known as a resource manager (RM).
SetComplete() function should be called as often as possible so that MTS keeps less object around and it helps scalability. Servers should get resources (database connection) as late as possible and gives them up as soon as possible. Servers should use roles and declaraticve security.
MTS Executive is implemented in mtxex.dll. The MTS Executive aand the application DLLs (MTS components) are loaded into the surrogate called mtx.exe.
Calling SetComplete() as often as possible allows MTS to get rid of objects that are no longer needed.
A resource dispenser provides an efficient way of acquiring and freeing shared, non persistent resources on a system. MTS introduces a new security model called declarative security. In this concept, users and/or groups are collected into a named sets called role. These roles are unique in each MTS package (collection of MTS components).
Administrator defines which users and groups are associated with each role by using MTS Explorer. MTS Explorer is also used for defining access rights about MTS components. Granularity of securable item can be an interface. MTS enforces role based security on each incoming call on every interfaces. MTS can query the role of the callee and verifies its credentials.
IsCallerInRole() function in IObjectContext interface allows server developer to get the role information about the caller.
Implementation inheritance is viewed as white-box resuse since developer needs to know very detailed information about the base classes while it is developing the derived classes. Interface inheritance expresses the type relationships. Interfaces model the abstract requests that can made by clients. Interface-based model separtes "what the object is" from "Hos the object implements". Since the implementation details are never disclosed, interface based programming can be viewed as black-box approach.
Microsoft Transaction Server (formely known as Viper) is available since Microsoft windows Nt(R) version 4.0 Service Pack 4.
Besides MTS is a server and manages transactions, it also provides an object manager functionality, a thread pool management, a security management model to define a basis for distributed application framework (It was the first step towards a COM+ framework).
A package in MTS contains one or more components and each component can contain one more classes, called as MTS Object.
Roles, set at the package level, are central to the MTS security model. A role is an abstraction that defineds a logical group of users.
Role is a list of NT user groups and accounts that should have access to components in that package. In declarative security, a different account is defined for each component to act on the original user's behalf and this is called identity of the package containing the component.
We can set up roles and identities that allow each type of user access only to the appropriate components. A mapping between roles and components allow us to define which roles can access which components. Components do not have their own identities, they use the identity of the package. MTS defines a special package named System for administrative access to MTS Explorer. By including roles in System package it is possible to restrict the administrative access to particular accounts. Each component within a package checks the validity of each user against a set of roles defined for that package.
IsSecurityEnabled() function in IObjectContext interface returns FALSE, if the component is installed in in-process server.
Microsoft Message Queue Server is included with Windows NT Server 4.0 for Message Queuing Middleware support. Message Queuing Middleware (MQM) supports reliable, asycnhronous, and loosly coupled communication service between applications. One application can send a message to another application, at any location and message can contain data, control information, or any kind of data. In MQM framework, each application has its own message queue and an application sends a message to the message queue of another application. Middleware takes care of message transmission and management of queue objects. MQM can provide location transparency, tier separation, scalability, independence, and support for event-driven applications. In MQM environment, Queue Manager (QM) is responsible for storing and managing queues, therefore QM supports the following services:
MQM provides strong request and reply delivery guarantees so that middleware transmit messages only-once and in order. A typical scenarios where MQM functionality is desired can be summarized as follows:
Since applications in MQM framework communicates through messages and messages are kept in queues during the transmission, MQM brings very crucial features such as:
When MOM starts leaving in distributed transactional environment, then the operations can be chopped down to following pieces for the message passing between application A to application B for request and response:
When carefully inspected, it is not hard to see that step 4 and 5 actually correspond to step 2 and 3. Now let us look at the Microsoft Message Queue Server and its internals.
Microsoft Message Queue Server (MSMQ) refers to MSMQ network as an enterprise. An enterprise is divided into connected networks where all computers communicates through the same protocol and sites where computers are assumed to be physically close to each other.
A central server called Primary Enterprise Controller (PEC) maintains the enterprise configuration database. One server in each site designated as Primary Site Controller (PSC) for maintaining the local copy of configuration. For fault tolerance, enterprise may have one or more Backup Site Controllers (PSC). For all controllers, MSMQ requires a minimal SQL Server installation as SQL Server is used for keeping configuration information. In addition to these servers, MSMQ defines two servers Routing Server and COnnector Server. Both of them are responsible for message delivery through the system. Connector Servers are designed to work with non-Windows systems.
MSMQ defines two kinds of client: Dependent and Independent. Dependent clients should be connected to a routing server all the time as Independent clients is not and their messages are kept by the servers until they connect to the system to collect them.
MSMQ defines three default additional queues on each site:
DCOM will have an option to use MSMQ for transmission technology.
PEC stores details of the queue locations and the other servers and clients that have access to the queues and messages. PEC supplies information about the entire enterprise to all other systems when they need to locate or work eith a queue and is responsible for the routing of messages between queues. Sites consist of machines that have generally fast an reliable links between them. For routing purposes, a cost is associated with each line connecting sites. Connector Server acts as a bridge between native and foreign queues. PEC also provides PSC and RS services for the site where it is located. Site Controllers keep the part of the enterprise information. Each site can contain both a Primary and Backup Site Controller. Routing Servers do not hold any information, and are used only to route messages between queues. An independent client can move from one site to another, and it is automatically configured by MSMQ to allow messages to be sent from it while it is connected to the new site.
Message Queue can be disk-based (recoverable) or RAM-based (express).
In NT4.0, Message Queue Information Store (MQIS) holding the location information about each queue in the enterprise is kept on SQL Server. In NT5.0, Active Directory will be used for this purpose.
Site link costs can be st to ant number between zero (no cost) and 999999 (highest cost).
Queues can not be created on Dependent Clients, which themselves demand a permanent connection to the enterprise.
MSMQMessage object represents the message and encapsulated its properties.
Once the message is complete, it is sent to the appropriate queue where it resides either until another application collects it, or until it expires. An acknowledgement from the recipient can be requested. The sender specifies the queue that is to be used for reply and recipients can reply with custom acknowledgements.
Transactional requirement is done with a simple setting.
Message Queue Explorer is used for administrative purposes.
A general interfaces in MSMQ can be summarized as follows:
Open function in MSMQQueueInfo interface allows user to open a queue for peek, send, and retrieve mode. In peek mode, user can only see the messages without delete permission. In send mode, user can send a message to the queue. In retrieve mode, user can see and retrieve messages from the queue. Open function also asks the permission for concerrent users of the same queue. In peek and send mode, queue can service other users. In retrieve mode, only the user can retrieve messages from the queue. Retrieve mode is not sharable with other users.
Send() function in MSMQMessage interface is used for sending a message to the particular queue. It accepts a pointer to the MSMQQueue interface and the optional transaction parameter. MSMQMessage contains the following parameters:
A directory is an information source to store information about objects which can encapsulate files, printer or special server objects. There are two major users of them: users who want to use them and administrators who want to manage them in the system. Directory service provides an atribute based searching capability to its users (including administrators) so that user can obtain the whole address information about objects with partial knowledge about them. Directory service should provide a support for the following issues:
The Active Directory service is included with Windows NT Server version 5.0. The Active Directory provides a solution for all requirements. The Active Directory defines some terms for clarification of the whole process:
Active Directory keeps the objects in its stores. An object contains a named set of attributes decribed by the subject that is idenfied by the directory object. Objects which can contain other objects are called containers. Active Directory requires a class definition for the object that it is supposed to be stored. This definition gives the required attributes, optional attributes, their type and range if applicable. This definition (schema) is also fits into an object oriented paradigm so that it is possible to create schema hierarchies based on inheritance. Active Directory keeps schema definitions in the same storage. The schema defines two crucial propoerties for each object:
Active Directory replicates the naming contexts. Naming context is any contiguous subtree of the directory. Active Directory also requires a naming context definition for the users so that it can provide necessary security restrictions. Therefore, any Active Directory Server contains the following contexts:
The Active Directory uses ACL mechanism for security enforcement. Each object in the directory is associated with ACLs so that any user's credentials can be compared with this ACLs to find whether user has permission to conduct the operation on the object. Authorization is defined based on:
ACLs on directory objects contain ACEs that apply to the object as a whole and ACEs that apply to the individual attributes of the object. Active Directory Security supports delgation and inheritance. Delegation allows a higher administrative authority to grant specific administrative rights. Inheritance allows a propagation of ACE to all children of the container that the certain permissions are given so that a higher administrative authority can give certain permissions of administrative operations on all the objects under the subtree of the container in one operation.
Active Directory introduces an object called Directory System Agent (DSA) to provide a service its users so that users never sees which storage technology is used by Active Directory. This separation is important for easy upgrade and multi platform support. DSA's interface supports searching, reading and writing capabilities. Therefore, a user can conduct a search on the directory with partial information (a set of attribute-value pairs), read permitted information about the object, update/change a value of permitted attributes of an object, add a new object to the hierarchy provided that user has write permission to the particular subtree of the directory.
Active Directory provides several APIs for several programming languages:
ADSI provides a support for accessing LDAP, Windows NT 4.0, NetWare 3.x, and NetWare 4.x directories from the same API. Other directories are defined in virtual containers which allow any LDAP compliant directory to be accessed via the AD. Each virtual container defines the place of the directory in AD and the DNS name of the server.
Although Distinguished Name (DN) of an object includes enough information to locate a replica of the partition that keeps the object, users may not have the whole DN of an object instead they may know the value of several attributes but they still would like to access the object. The Global Catalog (GC) is designed for this purpose. The GC contains a partial replica of every user naming context, the schema and configuration naming contexts. The GC keeps a very small number of predefined attributes associated with objects. Administrators can also add the additional attributes which have to be stored in GC. Recommended practice is to include attributes which are likely to be unique for each object for the particular object class.
Replication technique used in AD is known as multimaster replication where all the replicas are writable. Coherence problem between replicas are answered with Update Sequence Numbers (USN). Each Active Directory Server maintains a 64-bit number and increments this number with each attribute update. Each attribute also keeps its version number. The synchronization problem between replicas are resolved with USNs.
Active Directory supports various naming formats:
Active Directory service is part of the distributed computing environment that provides a way to locate and identify the users and resources available in the system. Class Store directory in AD is used for keeping the name (CLSID or ProgID) and locations of COM objects installed on the network. COM uses the Class Store to locate and install the COM objects that users are allowed to use on their machines automatically.