Microsoft component object model tutorials


















Please rate your experience Yes No. Any additional feedback? In this article. Controls and Property Pages. COM Language Translations. Privacy policy. COM allows an object to expose its functionality to other components and to host applications. It defines both how the object exposes itself and how this exposure works across processes and across networks. COM also defines the object's life cycle. Interfaces — the mechanism through which an object exposes its functionality.

Indeed, you could have implemented CSpaceship strictly with embedding. Nesting, however, brings to the party two advantages:. Nested class member functions can access parent class data members without the need for CSpaceship pointer data members, and.

The nested classes are neatly packaged along with the parent while remaining invisible outside the parent. Look at the code below for the GetPosition member function. Notice also the double scope resolution operators, which are necessary for nested class member functions. The compiler always knows the offset from the beginning of parent class data to the beginning of nested class data. Now suppose you have two interface pointers, pMot and pVis , for a particular CSpaceship object.

Don't worry yet about how you got these pointers. You can call interface member functions in the following manner:. What's happening under the hood? For each object, there's a pointer to the object's data, the first element of which is a pointer to the class's vtable. The pointer relationships are shown here. Figure 1: A COM objects pointer relationship.

Theoretically, it's possible to program COM in C. If you look at the Windows header files, you'll see code such as this:. It's important to realize, however, that in neither language do the interface declarations have data members, constructors, or destructors.

Therefore, you can't rely on the interface having a virtual destructor, but that's not a problem because you never invoke a destructor for an interface. Let's get back to the problem of how to obtain your interface pointers in the first place. COM declares a special interface named IUnknown for this purpose. As a matter of fact, all interfaces are derived from IUnknown , which has a pure virtual member function, QueryInterface , which returns an interface pointer based on the interface ID you feed it.

Once the interface mechanisms are hooked up, the client needs to get an IUnknown interface pointer at the very least or a pointer to one of the derived interfaces. Here is the new interface hierarchy, with IUnknown at the top:. What do the vtables look like after this is done? For each derived class, the compiler builds a vtable with the base class function pointers on top, as shown here.

GetClassObject can get the interface pointer for a given CSpaceship object by getting the address of the corresponding embedded object. Here's the code for the QueryInterface function in XMotion :. Thus, if two IUnknown pointers match, you can assume that they refer to the same object.

Now your client program can call QueryInterface to obtain an IVisual pointer, as shown here:. Notice that the client uses a CSpaceship object, but it never has an actual CSpaceship pointer.

Thus, the client cannot directly access CSpaceship data members even if they're public. Notice also that we haven't tried to delete the spaceship object yet, that will come shortly. There's a special graphical representation for interfaces and COM classes. Interfaces are shown as small circles or jacks with lines attached to their class. The IUnknown interface, which every COM class supports, is at the top, and the others are on the left.

The CSpaceship class can be represented something like this. Figure 3: Class and its interfaces. COM interfaces don't have virtual destructors, so it isn't cool to write code like the following:. When the client program is finished with the pointer, it calls Release. This means that it isn't necessary to call AddRef after the object is first constructed. A client program should call AddRef , however, if it makes a copy of an interface pointer.

Class Factories. Object-oriented terminology can get a little fuzzy sometimes. COM carries with it the notion of a " class object ," which is sometimes referred to as a " class factory ". From this interface definition, the Microsoft IDL compiler generates header files for use by applications using that interface, and source code to create proxy and stub objects that handle remote procedure calls.

IDL is only a tool for the convenience of the interface designer and is not central to COM's interoperability. It simply saves the developer from manually creating header files for each programming environment and from creating proxy and stub objects by hand. COM is not a specification for how applications are structured, it is a specification for how applications interoperate.

For this reason, COM is not concerned with the internal structure of an application. That is the job of the programmer, and also depends on the programming languages and development environments used. Conversely, programming environments have no set standards for working with objects outside the immediate application. Generally, other programming languages are the same. COM, through language-independent interfaces, picks up where programming languages leave off, providing network-wide interoperability of components making up an integrated application.

The core of the Component Object Model is a specification for how components and their clients interact. As a specification it defines a number of other standards for interoperability of software components:.

In general, only one vendor needs to, or should, implement a COM Library for any particular operating system. Also, it is important to note that COM draws a very clear distinction between:.

Therefore, developers are not constrained to use new and specific models for the services of different operating systems, yet they can develop components that interoperate with components on other platforms.

All in all, only with a binary standard on a given platform and a wire-level protocol for cross-machine component interaction can an object model provide the type of structure necessary for full interoperability between all applications and between all different machines in a network.

With a binary and network standard, COM opens the doors for a revolution in innovation without a revolution in programming or programming tools. Implementation inheritance--the ability of one component to "subclass" or inherit some of its functionality from another component--is a very useful technology for building applications.

Implementation inheritance, however, can create many problems in a distributed, evolving object system. The problem with implementation inheritance is that the "contract" or relationship between components in an implementation hierarchy is not clearly defined; it is implicit and ambiguous. When the parent or child component changes its behavior unexpectedly, the behavior of related components may become undefined.

This is not a problem when the implementation hierarchy is under the control of a defined group of programmers who can make updates to all components simultaneously. But it is precisely this ability to control and change a set of related components simultaneously that differentiates an application, even a complex application, from a true distributed object system. So although implementation inheritance can be a very good thing for building applications, it is not appropriate for a system object model that defines an architecture for component software.

In a system built of components provided by a variety of vendors, it is critical that a given component provider be able to revise, update, and distribute or redistribute a product without breaking existing code in the field that is using the previous revision or revisions of that component.

In order to achieve this, it is necessary that the actual interface on the component used by such clients be crystal clear to both parties. Otherwise, how can the component provider be sure to maintain that interface, and thus not break the existing clients? From observation, the problem with implementation inheritance is that it is significantly easier for programmers not to be clear about the actual interface between a base and derived class than it is to be clear.

This usually leads implementors of derived classes to require source code to the base classes; in fact, most application framework development environments that are based on inheritance provide full source code for exactly this reason.

The bottom line is that inheritance, although very powerful for managing source code in a project, is not suitable for creating a component-based system where the goal is for components to reuse each others' implementations without knowing any internal structures of the other objects. Inheritance violates the principle of encapsulation , the most important aspect of an object-oriented system. This is the classic paradigm of reuse in implementation inheritance: A base class periodically makes calls to its own virtual functions, which may be overridden by its derived classes.

In practice, in such a situation CDerived can become, and therefore often will become, intimately dependent on exactly when and under what conditions Sample will be invoked by the class CBase. If, at present, all such Sample invocations by CBase are intended long-term as hooks for the derived class, there is no problem.

There are two cases, however; either the implementation of CBase::Sample is implemented as. If the implementation of CBase::Sample is not empty, it is carrying out some useful and likely needed transformation on the internal state of CBase. Thus, it is questionable whether all of the invocations of Sample are for the support of derived classes; some of them instead are likely to be only for the purpose of carrying out this transformation.

That transformation is part of current implementation of CBase. Thus, in summary, CDerived becomes coupled to details of that current implementation of CBase ; the interface between the two is not clear and precise. Further coupling comes from the fact that the implementation of CDerived::Sample , in addition to performing its own role, must be sure to carry out the transformation carried out in CBase::Sample.

It can do this by reimplementing the code in CBase::CBase itself, but this causes obvious coupling problems. Alternatively, it can itself invoke the CBase::Sample method:. However, it is very unclear what is appropriate or possible for CDerived to do in the areas marked "Do some work" and "Do other work. If, in contrast, CBase::Sample is empty, we likely are not in immediate danger of surreptitious coupling.

In the implementation of CBase , invoking Sample clearly serves no immediately useful purpose, and so it is likely that indeed all invocations of Sample in CBase are only for the support of CDerived. Consider, however, the case in which the CDerived class is reused:. This is the architectural heart of the problem observed in practice that leads to a view that implementation inheritance is unacceptable as the mechanism by which independently developed binary components are reused and refined.

Both of these reuse mechanisms allow objects to exploit existing implementation while avoiding the problems of implementation inheritance. See Appendix 2 of this article for an overview of these alternate reuse mechanisms.

The key point to building reusable components is black-box reuse , which means the piece of code attempting to reuse another component knows nothing, and needs to know nothing, about the internal structure or implementation of the component being used. In other words, the code attempting to reuse a component depends upon the behavior of the component and not the exact implementation.

As illustrated in Appendix 1, implementation inheritance does not achieve black-box reuse. For convenience, the object being reused is called the inner object and the object making use of that inner object is the outer object. These two mechanisms are illustrated in Figures 8 and 9. The important part of both these mechanisms is how the outer object appears to its clients.

As far as the clients are concerned, both objects implement interfaces A , B , and C. Furthermore, the client treats the outer object as a black box, and thus does not care, nor does it need to care, about the internal structure of the outer object--the client only cares about behavior.

Figure 8. Containment of an inner object and delegation to its interfaces. Aggregation is almost as simple to implement. The solution is for the inner object to delegate IUnknown calls in its own interfaces, but also allow the outer object to access the inner object's IUnknown functions directly.

COM provides specific support for this solution. Figure 9. Aggregation of an inner object where the outer object exposes one or more of the inner object's interfaces as its own.



0コメント

  • 1000 / 1000