Chapter 4: Using .NET Components in COM Applications

team lib

Chapter 3 showed you how COM objects can be used in .NET code and how using COM objects facilitates the process of transitioning from COM-based code to .NET applications. This chapter will show how you can also present .NET components as COM objects so that they can be used in COM client code.

The COM Callable Wrapper

You saw in Chapter 3 how the Runtime Callable Wrapper (RCW) wraps COM components to expose them to .NET. This chapter looks at COM Callable Wrappers (CCWs), which are used to wrap .NET components to expose them to COM client code.

There is exactly one COM Callable Wrapper object per .NET object, although the CCW can be used by multiple COM clients at any one time. The .NET object can be directly used by other .NET clients at the same time as its being used by the CCW, as shown in Figure 4-1.

click to expand
Figure 4-1: A COM Callable Wrapper (CCW) can be used by multiple COM clients.

Because .NET components know nothing of COM or the rules that COM objects must obey, the CCW handles all the low-level operations that must be handled by COM components, which includes handling object identity, object lifetime, and COM interface issues.

Object Identity

COM Callable Wrappers are created on the unmanaged heap. They are created this way so that client code can refer to interface pointers directly and to ensure interface pointers on the CCW obey the COM rules for interfaces (for example, the rule that an object must use the same pointer for an interface throughout the lifetime of the component).

Another identity- related aspect of CCWs is that they expose an IUnknown interface, which can be used as the COM identity for the .NET object. The actual .NET component referenced by the CCW is allocated on the managed heap as usual, and the garbage collector can move it around in memory as necessary. Thus, without CCWs, .NET components would break the COM identity rule, which requires an object to return the same IUnknown pointer for its lifetime.

Object Lifetime

COM rules state that COM objects should maintain a reference count, which reflects the number of interface pointers being used by clients. When a client has finished with an interface pointer, it signals the fact by calling the interfaces Release method, which causes the COM object to decrement the reference count. When the reference count drops to zero, no clients hold pointers on the object and the object can terminate itself. Note that its up to the COM component to manage its own lifetime.

In contrast, the lifetimes of .NET components are managed by the common language runtime, which keeps track of references held by clients. When there are no more client references, the component can be reclaimed by the garbage collector. In the .NET world, it isnt the responsibility of the component to manage its own lifetime.

The CCW acts as a COM object, with a reference count that reflects the interfaces handed out to COM clients. It also holds a reference on the .NET component. When the CCW has no more COM clients, its reference count drops to zero; at this point, it releases the reference it holds on the .NET component.

Standard COM Interfaces on .NET Components

As well as exposing the methods supported by the .NET component, the CCW will also expose a number of common COM interfaces that clients will expect to find on a COM object, as shown in Figure 4-2.

click to expand
Figure 4-2: A COM Callable Wrapper (CCW) implements a number of standard COM interfaces.

The COM interfaces that are always implemented by a CCW are listed in Table 4-1.

Table 4-1: COM Interfaces Always Implemented by CCWs

Interface

Description

IUnknown

Provides the fundamental lifetime management and interface navigation functionality for COM interfaces.

IDispatch

Provides a mechanism for late binding to objects.

IProvideClassInfo

Provides a way for clients to obtain an ITypeInfo interface pointer on an object.

ISupportErrorInfo

Enables client code to determine whether a COM object supports the IErrorInfo interface.

IErrorInfo

Provides rich error information. All .NET components support this interface, passing GUID_NULL for the ID of the interface that raised the error.

ITypeInfo

Provides type information for the class.

In addition, several other interfaces can be exposed, as listed in Table 4-2.

Table 4-2: COM Interfaces Sometimes Implemented by CCWs

Interface

Description

IDispatchEx

If the .NET component implements the IExpando interface, the CCW will implement IDispatchEx . This provides an extension to IDispatch that allows enumeration, addition, deletion, and case-sensitive calling of members .

IConnectionPointContainer and IConnectionPoint

These interfaces will be implemented if the .NET component is a source of events. See the section Exposing .NET Events in COM later in the chapter for more details.

IEnumVARIANT

This interface provides a COM mechanism for iterating over collections and will be implemented by the CCW if the .NET component implements the IEnumerable .NET interface.

A .NET component can override the standard implementation of any of these interfaces (except IDispatch and IUnknown ) by providing a custom implementation. The CCW will always provide the implementation of IDispatch and IUnknown .

Custom Interfaces on .NET Components

All access to COM objects is via interface methods. It follows that for .NET components to be exposed as COM components, the methods implemented by the .NET component must be exposed using an interface. How this is done depends on whether the .NET component implements its methods directly or via .NET interfaces.

If a .NET class implements interfaces, those interfaces will be exported as COM interfaces. This process is shown in Figure 4-2, where the .NET component implements IMyInterface . This interface is in turn implemented by the CCW and is accessible to COM clients. The conversion process applied to .NET interfaces to expose them to COM clients is detailed in the section Exporting Interfaces later in the chapter.

The Class Interface

Where a .NET class implements methods directly, the export process can create an interface through which to make the class methods visible. This interface is called the class interface , and by default the export process will generate a pure dispatch interface (a dispinterface ) as the class interface. The name of this interface will be the name of the class with a leading underscore (for example _Car for a class called Car ).

The type of interface generated for the class interface, and even determining whether it is generated at all, can be controlled by using the ClassInterface attribute on .NET classes. Examples in the section Generating and Using COM Callable Wrappers later in the chapter will show this attribute in use, as well as show what the generated class interface looks like.

Problems with Using the Class Interface

Although using the class interface makes it easy for .NET programmers to expose .NET components as COM components, you need to be aware of possible problems.

The COM interface rules state that an interface is immutable once it has been definedit must not change its layout or composition. The class interface, however, is generated automatically from the .NET class definition, so the layout and composition of the interface could change if the makeup of the class is changed.

COM client code is normally written to assume that interfaces wont change, so if the evolution of a .NET component causes changes in the generated class interface, this could end up breaking client code. To get around this problem, switch off the generation of a class interface and define .NET interfaces for the methods you want exposed to COM.

In addition, you should beware of caching the dispatch IDs ( dispIds ) assigned to members in class interfaces. If the layout of the class changes, the dispIds assigned to members might change as well. This isnt a problem for late-bound clients, which discover dispIds at run time, but it might break client code that relies on compile-time caching of IDs.

The way around this problem is to apply the ClassInterfaceType.AutoDispatch attribute to the interface. This technique generates a dispinterface for the class interface, but it prevents the description of the dispatch interface from being placed in the type library. The lack of interface information in the type library means that client code cannot cache dispIds at compile time.

Note 

These problems with dispatch IDs mean that the class interface is most useful for purely late-bound COM clients, such as scripting languages. These clients always query for the dispatch IDs of interface members at run time, so it doesnt matter to them whether these IDs have changed.

Finally, if you do use the class interface, be careful using dual class interfaces (specified using the ClassInterfaceType.AutoDual attribute). A description of the interface will appear in the generated type library, which could encourage client code to cache dispIds. In addition, client code can break when dual interfaces are used if changes are made to the base class of the .NET component.

 
team lib


COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

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